@validation-os/core 0.16.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-OBAVYIST.js → chunk-TCKAMOAK.js} +79 -10
- package/dist/chunk-TCKAMOAK.js.map +1 -0
- package/dist/derivation/index.d.ts +1 -1
- package/dist/derivation/index.js +5 -1
- package/dist/{index-COstaDa6.d.ts → index-DhQyc0Gj.d.ts} +36 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-OBAVYIST.js.map +0 -1
|
@@ -7,6 +7,7 @@ var derivation_exports = {};
|
|
|
7
7
|
__export(derivation_exports, {
|
|
8
8
|
COMMITMENT_FOUND: () => COMMITMENT_FOUND,
|
|
9
9
|
COMPLETENESS_SLOTS: () => COMPLETENESS_SLOTS,
|
|
10
|
+
CONFIDENCE_FLOOR_BY_STAGE: () => CONFIDENCE_FLOOR_BY_STAGE,
|
|
10
11
|
DEFAULT_QUESTION_TYPE: () => DEFAULT_QUESTION_TYPE,
|
|
11
12
|
INFERABLE_QUESTION_TYPES: () => INFERABLE_QUESTION_TYPES,
|
|
12
13
|
KILL_LANE_THRESHOLD: () => KILL_LANE_THRESHOLD,
|
|
@@ -26,6 +27,7 @@ __export(derivation_exports, {
|
|
|
26
27
|
completenessSlotPresence: () => completenessSlotPresence,
|
|
27
28
|
confidence: () => confidence,
|
|
28
29
|
confidenceAttribution: () => confidenceAttribution,
|
|
30
|
+
confidenceFloorForStage: () => confidenceFloorForStage,
|
|
29
31
|
confidenceTrajectory: () => confidenceTrajectory,
|
|
30
32
|
deriveBeliefStage: () => deriveBeliefStage,
|
|
31
33
|
derivedImpacts: () => derivedImpacts,
|
|
@@ -137,14 +139,33 @@ var RISK_THRESHOLD_BY_STAGE = {
|
|
|
137
139
|
Maturity: 5
|
|
138
140
|
// defensive, often regulatory — strongest evidence
|
|
139
141
|
};
|
|
142
|
+
var CONFIDENCE_FLOOR_BY_STAGE = {
|
|
143
|
+
Discovery: 10,
|
|
144
|
+
// any signal counts for a two-way door
|
|
145
|
+
Validation: 25,
|
|
146
|
+
// need a real reading, not just vibes
|
|
147
|
+
Scale: 40,
|
|
148
|
+
// need solid evidence before scaling
|
|
149
|
+
Maturity: 60
|
|
150
|
+
// need strong evidence for a one-way door
|
|
151
|
+
};
|
|
140
152
|
function riskThresholdForStage(stage) {
|
|
141
153
|
if (stage && stage in RISK_THRESHOLD_BY_STAGE) {
|
|
142
154
|
return RISK_THRESHOLD_BY_STAGE[stage];
|
|
143
155
|
}
|
|
144
156
|
return RISK_THRESHOLD_BY_STAGE.Maturity;
|
|
145
157
|
}
|
|
146
|
-
function
|
|
147
|
-
|
|
158
|
+
function confidenceFloorForStage(stage) {
|
|
159
|
+
if (stage && stage in CONFIDENCE_FLOOR_BY_STAGE) {
|
|
160
|
+
return CONFIDENCE_FLOOR_BY_STAGE[stage];
|
|
161
|
+
}
|
|
162
|
+
return CONFIDENCE_FLOOR_BY_STAGE.Maturity;
|
|
163
|
+
}
|
|
164
|
+
function hasClearedThreshold(risk2, stage, confidence2) {
|
|
165
|
+
const riskCleared = risk2 <= riskThresholdForStage(stage);
|
|
166
|
+
if (confidence2 === void 0) return riskCleared;
|
|
167
|
+
const confCleared = confidence2 >= confidenceFloorForStage(stage);
|
|
168
|
+
return riskCleared && confCleared;
|
|
148
169
|
}
|
|
149
170
|
|
|
150
171
|
// src/derivation/completeness.ts
|
|
@@ -213,7 +234,16 @@ var RULES = [
|
|
|
213
234
|
/\bregulat(?:or|ion)\s+(?:prohibits|rul(?:es|ed)\s+against)\b/,
|
|
214
235
|
/\bregulator\b.*\bagainst\b/,
|
|
215
236
|
/\bcompliance\s+audit\s+fails?\b/,
|
|
216
|
-
/\bprohibits?\b.*\bscoring\b
|
|
237
|
+
/\bprohibits?\b.*\bscoring\b/,
|
|
238
|
+
// Positive bar patterns (the register's rightIf text):
|
|
239
|
+
/\bregulat(?:or|ion)s?\s+(?:approve|accept|confirm|permit)\b/,
|
|
240
|
+
/\bfca\b.*\bconfirm\b/,
|
|
241
|
+
/\blegal\s+opinion\b.*\bstate/i,
|
|
242
|
+
/\bnon[-\s]?compliant\b.*\boutputs?\b/,
|
|
243
|
+
/\bconduct\s+layer\b.*\b(?:safe|within|comply)\b/,
|
|
244
|
+
/\bconsumer\s+duty\b/,
|
|
245
|
+
/\bguardrails?\b.*\b(?:regulat|comply|within)\b/,
|
|
246
|
+
/\beducation\b.*\bguidance\b.*\bwithin\b/
|
|
217
247
|
]
|
|
218
248
|
},
|
|
219
249
|
{
|
|
@@ -229,7 +259,15 @@ var RULES = [
|
|
|
229
259
|
/\bdon't\s+commit\b/,
|
|
230
260
|
/\boffered\s+users?\s+pay\b/,
|
|
231
261
|
/\bpay(?:ing)?\s+(?:rate|share)?\s*(?:is\s+)?below\b/,
|
|
232
|
-
/\bfewer\s+than\s+\d+\s+of\s+\d+\s+offered\s+users?\s+pay\b
|
|
262
|
+
/\bfewer\s+than\s+\d+\s+of\s+\d+\s+offered\s+users?\s+pay\b/,
|
|
263
|
+
// Positive bar patterns:
|
|
264
|
+
/\bsign\s+(?:a\s+)?(?:design[-\s]?partner|pilot)\s+agreement\b/,
|
|
265
|
+
/\bsigned\s+intent\b/i,
|
|
266
|
+
/\bcommit(?:s|ting)?\s+(?:before|to\s+pay|to\s+a)\b/,
|
|
267
|
+
/\badopt\s*(?:and\s*)?pay\b/,
|
|
268
|
+
/\bpay\s+for\b.*\b(?:layer|harness|governed)\b/,
|
|
269
|
+
/\bagree\s+(?:in\s+writing|to\s+ingest|to\s+sign)\b/,
|
|
270
|
+
/\b(?:design|pilot)\s+partner\b.*\bagreement\b/
|
|
233
271
|
]
|
|
234
272
|
},
|
|
235
273
|
{
|
|
@@ -240,7 +278,16 @@ var RULES = [
|
|
|
240
278
|
/\bsustained\s+usage\s+falls?\s+below\b/,
|
|
241
279
|
/\bretention\s+(?:drops?|falls?)\s+below\b/,
|
|
242
280
|
/\bweek-\d+\s+retention\s+drops?\s+below\b/,
|
|
243
|
-
/\bdisuse\b
|
|
281
|
+
/\bdisuse\b/,
|
|
282
|
+
// Positive bar patterns:
|
|
283
|
+
/\bengage\s+(?:turn[-\s]by[-\s]turn|with|positively)\b/i,
|
|
284
|
+
/\bengage(?:s|ment)?\s+with\b/i,
|
|
285
|
+
/\breact\s+positively\b/i,
|
|
286
|
+
/\bcomfortable\s+with\b.*\b(?:bank|storing|context)\b/i,
|
|
287
|
+
/\bchoose\s+(?:voice|as\s+their\s+primary)\b/i,
|
|
288
|
+
/\bsustained\s+(?:retention|usage|engagement)\b/i,
|
|
289
|
+
/\bcome\s+back\b.*\bsession/i,
|
|
290
|
+
/\bprefer\b.*\b(?:modality|interface|experience)\b/i
|
|
244
291
|
]
|
|
245
292
|
},
|
|
246
293
|
{
|
|
@@ -251,7 +298,13 @@ var RULES = [
|
|
|
251
298
|
/\bvariant\s+doesn'?t\s+outperform\s+control\b/,
|
|
252
299
|
/\bdiffer\s+from\s+control\b/,
|
|
253
300
|
/\boutperform\s+control\b/,
|
|
254
|
-
/\btreatment\s+vs\.?\s+control\b
|
|
301
|
+
/\btreatment\s+vs\.?\s+control\b/,
|
|
302
|
+
// Positive bar patterns:
|
|
303
|
+
/\bbeats?\s+(?:a\s+)?\w+[-\s]only\s+(?:model|baseline|control)\b/i,
|
|
304
|
+
/\brelative\s+lift\b/i,
|
|
305
|
+
/\breplicated\s+in\b.*\bcohort/i,
|
|
306
|
+
/\bwins?\s+≥?\s*\d+\s*[×x]\b/i,
|
|
307
|
+
/\bframing\s+wins\b/i
|
|
255
308
|
]
|
|
256
309
|
},
|
|
257
310
|
{
|
|
@@ -263,7 +316,12 @@ var RULES = [
|
|
|
263
316
|
/\bthe\s+(?:rate|share|proportion)\s+is\s+below\b/,
|
|
264
317
|
/\b(?:in\s+)?surveyed\s+teams\b/,
|
|
265
318
|
/\bshare\s+(?:of\s+\w+\s+)?(?:is\s+)?below\b/,
|
|
266
|
-
/\bproportion\s+is\s+below\b
|
|
319
|
+
/\bproportion\s+is\s+below\b/,
|
|
320
|
+
// Positive bar patterns:
|
|
321
|
+
/\b[≥>]\s*\d+%\s+of\b/i,
|
|
322
|
+
/\b[≥>]\s*\d+\s+of\b/i,
|
|
323
|
+
/\bnamed\b.*\b(?:as|in)\s+a\s+top[-\s]\d\b/i,
|
|
324
|
+
/\b[≥>]\s*\d+\s+(?:of\s+\d+\s+)?(?:institutions?|consumers?|users?|buyers?|banks?|teams?)\b/i
|
|
267
325
|
]
|
|
268
326
|
},
|
|
269
327
|
{
|
|
@@ -274,7 +332,11 @@ var RULES = [
|
|
|
274
332
|
/\bcan'?t\s+be\s+built\b/,
|
|
275
333
|
/\bcan'?t\s+ingest\b/,
|
|
276
334
|
/\bprototype\s+can'?t\s+be\s+built\b/,
|
|
277
|
-
/\bcan'?t\s+\w+\s+(?:unaided|without\s+(?:help|regressions))\b
|
|
335
|
+
/\bcan'?t\s+\w+\s+(?:unaided|without\s+(?:help|regressions))\b/,
|
|
336
|
+
// Positive bar patterns:
|
|
337
|
+
/\bcomplete\s+the\s+(?:flow|setup|wizard)\b.*\b(?:unaided|without\s+help)\b/i,
|
|
338
|
+
/\bingest\b.*\b(?:rows?|records?)\b.*\b(?:under|in)\s+\d+/i,
|
|
339
|
+
/\b(?:can|able\s+to)\s+(?:build|ship|deliver)\b.*\b(?:without|no)\s+(?:regress|break)/i
|
|
278
340
|
]
|
|
279
341
|
},
|
|
280
342
|
{
|
|
@@ -284,7 +346,12 @@ var RULES = [
|
|
|
284
346
|
/\bno\s+interviewee(?:s)?\s+(?:describe|report|mention)s?\b/,
|
|
285
347
|
/\bcan'?t\s+describe\s+the\s+mechanism\b/,
|
|
286
348
|
/\bno\s+one\s+reports\s+caring\b/,
|
|
287
|
-
/\bno\s+one\s+describes\b
|
|
349
|
+
/\bno\s+one\s+describes\b/,
|
|
350
|
+
// Positive bar patterns:
|
|
351
|
+
/\bindependently\s+states?\b.*\b(?:needs?|wants?|values?|sees)\b/i,
|
|
352
|
+
/\bindependently\s+(?:report|state|confirm)s?\b/i,
|
|
353
|
+
/\bsay\s+they'?d\s+(?:be\s+)?(?:comfortable|prefer)\b/i,
|
|
354
|
+
/\b(?:credible|independent)\b.*\b(?:states?|reports?|confirms?)\b/i
|
|
288
355
|
]
|
|
289
356
|
}
|
|
290
357
|
];
|
|
@@ -947,7 +1014,9 @@ export {
|
|
|
947
1014
|
isNonEvidence,
|
|
948
1015
|
ceilingAnchor,
|
|
949
1016
|
RISK_THRESHOLD_BY_STAGE,
|
|
1017
|
+
CONFIDENCE_FLOOR_BY_STAGE,
|
|
950
1018
|
riskThresholdForStage,
|
|
1019
|
+
confidenceFloorForStage,
|
|
951
1020
|
hasClearedThreshold,
|
|
952
1021
|
sign,
|
|
953
1022
|
isConcluded,
|
|
@@ -983,4 +1052,4 @@ export {
|
|
|
983
1052
|
assembleJourney,
|
|
984
1053
|
derivation_exports
|
|
985
1054
|
};
|
|
986
|
-
//# sourceMappingURL=chunk-
|
|
1055
|
+
//# sourceMappingURL=chunk-TCKAMOAK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/derivation/index.ts","../src/derivation/round.ts","../src/derivation/rung.ts","../src/derivation/completeness.ts","../src/derivation/strength.ts","../src/derivation/question-type.ts","../src/derivation/migrate.ts","../src/derivation/source-quality.ts","../src/types.ts","../src/derivation/confidence.ts","../src/derivation/attribution.ts","../src/derivation/progress.ts","../src/derivation/experiment-confidence.ts","../src/derivation/trajectory.ts","../src/derivation/impact.ts","../src/derivation/risk.ts","../src/derivation/portfolio.ts","../src/derivation/next-move.ts","../src/derivation/stage.ts","../src/derivation/journey.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 the instance's `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 isNonEvidence,\n ceilingAnchor,\n RISK_THRESHOLD_BY_STAGE,\n CONFIDENCE_FLOOR_BY_STAGE,\n riskThresholdForStage,\n confidenceFloorForStage,\n hasClearedThreshold,\n} from \"./rung.js\";\nexport {\n COMPLETENESS_SLOTS,\n completenessSlotPresence,\n missingCompletenessSlots,\n assumptionCompleteness,\n assumptionComplete,\n} from \"./completeness.js\";\nexport type { CompletenessSlot, CompletenessInput } from \"./completeness.js\";\nexport { sign, isConcluded, readingStrength } from \"./strength.js\";\nexport type { StrengthInput } from \"./strength.js\";\nexport {\n inferQuestionType,\n needsReview,\n INFERABLE_QUESTION_TYPES,\n DEFAULT_QUESTION_TYPE,\n} from \"./question-type.js\";\nexport { migrateRegister } from \"./migrate.js\";\nexport type {\n MigrationAssumption,\n MigrationReading,\n MigratedAssumption,\n MigratedReading,\n MigrationResult,\n} from \"./migrate.js\";\nexport { sourceQuality } from \"./source-quality.js\";\nexport {\n confidence,\n scoreAndDedupe,\n commitmentFactor,\n COMMITMENT_FOUND,\n W0,\n W0_BY_RUNG,\n w0ForRung,\n} from \"./confidence.js\";\nexport type { ConfidenceReadingInput, Scored } from \"./confidence.js\";\nexport { confidenceAttribution } from \"./attribution.js\";\nexport type {\n Attribution,\n AttributionReadingInput,\n Mover,\n MoverKind,\n} from \"./attribution.js\";\nexport { experimentProgress } from \"./progress.js\";\nexport type { BarLineInput, Progress } from \"./progress.js\";\nexport { experimentConfidence, MAX_STRENGTH } from \"./experiment-confidence.js\";\nexport type {\n ExperimentConfidenceBarInput,\n ExperimentConfidenceReadingInput,\n} from \"./experiment-confidence.js\";\nexport { confidenceTrajectory } from \"./trajectory.js\";\nexport type { TrajectoryPoint } from \"./trajectory.js\";\nexport { derivedImpacts } from \"./impact.js\";\nexport type { ImpactAssumptionInput } from \"./impact.js\";\nexport { risk } from \"./risk.js\";\nexport { beliefRisk, portfolioProgress } from \"./portfolio.js\";\nexport type {\n BeliefRisk,\n PortfolioBeliefInput,\n PortfolioProgress,\n} from \"./portfolio.js\";\nexport { rankNextMoves, KILL_LANE_THRESHOLD } from \"./next-move.js\";\nexport type {\n MoveKind,\n NextMove,\n NextMoveAssumptionInput,\n NextMoveExperimentInput,\n NextMoveDecisionInput,\n NextMoveInput,\n} from \"./next-move.js\";\nexport {\n beliefTestMeters,\n classifyStage,\n deriveBeliefStage,\n emptyTestMeter,\n} from \"./stage.js\";\nexport type {\n BeliefStage,\n BeliefStageInput,\n ConfSign,\n StageExperimentInput,\n StageKey,\n TestMeter,\n} from \"./stage.js\";\nexport { assembleJourney } from \"./journey.js\";\nexport type {\n AssembleJourneyInput,\n JourneyBeliefInput,\n JourneyEvent,\n JourneyEventKind,\n JourneyExperimentInput,\n} from \"./journey.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 * The question-type-aware evidence ladder (DEV-5890).\n *\n * Source of truth: `skills/_shared/ontology.yaml` → `vocabularies.rung` +\n * `vocabularies.question_type`. A rung is an evidence TYPE; magnitude band\n * (Low/Typical/High) is the intensity within a type. The band applies to\n * EVERY rung, so every rung looks up its anchor through the 3D table\n * `RUNG_ANCHOR[questionType][rung][band]`.\n *\n * Seven sub-ladders, one per question type. Evidence types that are\n * **non-evidence** for a question type carry anchor `0` across all bands —\n * they contribute `s=0` and are flagged at the UI/skill layer (not a write\n * blocker). The `0` is structural, not a separate flag.\n *\n * The rung vocabulary itself (`Talk`, `Desk research`, `Signed up`,\n * `Observed usage`, `Signed intent`, `Paying users`) is unchanged across\n * all sub-ladders — the same six rungs exist in every sub-ladder, with\n * different anchors (including `0` for non-evidence). This preserves the\n * existing `Rung` type and all reading-row machinery.\n *\n * Research backing: EBM GRADE (evidence hierarchies are question-relative),\n * Bayesian confirmation theory (probative value is hypothesis-relative),\n * qualitative research methods (qual is the ceiling for mechanism/existence\n * questions; saturation is a validity criterion), revealed preference\n * (sustained usage is the cleanest utility signal; stated intent is\n * non-evidence for WTP). See `docs/question-types.md`.\n *\n * W0 stays keyed by evidence type (within a question type), not by stage or\n * question type — see `confidence.ts` → `W0_BY_RUNG`. The learning rate\n * tracks the instrument's reliability, not the stakes or the claim kind.\n * Empirical-Bayes per-question-type W0 tuning is flagged as v2 (out of scope).\n */\nimport type { MagnitudeBand, QuestionType, Rung, Stage } from \"../types.js\";\n\n/**\n * The 3D anchor table — `RUNG_ANCHOR[questionType][rung][band]`.\n *\n * Seven sub-ladders. `0` entries are the non-evidence set for that question\n * type (the reading contributes `s=0`, flagged at the UI/skill layer). The\n * shape — what's probative, what's the ceiling, what's non-evidence — is\n * the decision; the exact values are illustrative anchors calibrated against\n * the research backing (see `docs/question-types.md`).\n *\n * Per the spec table:\n *\n * | Question Type | Talk L/T/H | Desk L/T/H | Signed up L/T/H | Observed usage L/T/H | Signed intent L/T/H | Paying users L/T/H | Ceiling |\n * |-------------------|--------------|--------------|-----------------|----------------------|----------------------|--------------------|-----------------------|\n * | Existence | 10/20/30 | 15/15/15 | 0/0/0 | 20/35/50 | 0/0/0 | 0/0/0 | Observed usage High(50)|\n * | Prevalence | 0/0/0 | 15/15/15 | 0/0/0 | 25/40/50 | 0/0/0 | 0/0/0 | Observed usage High(50)|\n * | CausalEffect | 0/0/0 | 0/0/0 | 0/0/0 | 30/50/70 | 30/50/70 | 50/70/90 | Paying users High(90) |\n * | WillingnessToPay | 0/0/0 | 0/0/0 | 30/50/70 | 0/0/0 | 50/70/85 | 75/88/99 | Paying users High(99) |\n * | ValueUtility | 10/20/30 | 0/0/0 | 0/0/0 | 30/50/70 | 0/0/0 | 0/0/0 | Observed usage High(70)|\n * | Regulatory | 0/0/0 | 30/50/70 | 0/0/0 | 0/0/0 | 0/0/0 | 0/0/0 | Desk research High(70)|\n * | Feasibility | 0/0/0 | 15/15/15 | 0/0/0 | 30/50/70 | 0/0/0 | 0/0/0 | Observed usage High(70)|\n */\ntype BandRecord = Record<MagnitudeBand, number>;\ntype RungAnchors = Record<Rung, BandRecord>;\n\nconst Z = { Low: 0, Typical: 0, High: 0 } as const satisfies BandRecord;\n\nfunction band(low: number, typical: number, high: number): BandRecord {\n return { Low: low, Typical: typical, High: high };\n}\n\n/** The seven sub-ladders, keyed by question type. */\nexport const RUNG_ANCHOR: Record<QuestionType, RungAnchors> = {\n Existence: {\n Talk: band(10, 20, 30),\n \"Desk research\": band(15, 15, 15),\n \"Signed up\": Z,\n \"Observed usage\": band(20, 35, 50),\n \"Signed intent\": Z,\n \"Paying users\": Z,\n },\n Prevalence: {\n Talk: Z,\n \"Desk research\": band(15, 15, 15),\n \"Signed up\": Z,\n \"Observed usage\": band(25, 40, 50),\n \"Signed intent\": Z,\n \"Paying users\": Z,\n },\n CausalEffect: {\n Talk: Z,\n \"Desk research\": Z,\n \"Signed up\": Z,\n \"Observed usage\": band(30, 50, 70),\n \"Signed intent\": band(30, 50, 70),\n \"Paying users\": band(50, 70, 90),\n },\n WillingnessToPay: {\n Talk: Z,\n \"Desk research\": Z,\n \"Signed up\": band(30, 50, 70),\n \"Observed usage\": Z,\n \"Signed intent\": band(50, 70, 85),\n \"Paying users\": band(75, 88, 99),\n },\n ValueUtility: {\n Talk: band(10, 20, 30),\n \"Desk research\": Z,\n \"Signed up\": Z,\n \"Observed usage\": band(30, 50, 70),\n \"Signed intent\": Z,\n \"Paying users\": Z,\n },\n Regulatory: {\n Talk: Z,\n \"Desk research\": band(30, 50, 70),\n \"Signed up\": Z,\n \"Observed usage\": Z,\n \"Signed intent\": Z,\n \"Paying users\": Z,\n },\n Feasibility: {\n Talk: Z,\n \"Desk research\": band(15, 15, 15),\n \"Signed up\": Z,\n \"Observed usage\": band(30, 50, 70),\n \"Signed intent\": Z,\n \"Paying users\": Z,\n },\n};\n\n/**\n * Is a rung **non-evidence** for a question type? A derived predicate — the\n * reading is allowed (not a write blocker) but contributes `s=0` and is\n * flagged at the UI/skill layer for human review (\"reclassify the assumption\n * or drop the reading\"). Non-evidence is `0` anchor across all bands, by\n * construction.\n */\nexport function isNonEvidence(\n questionType: QuestionType,\n rung: Rung,\n): boolean {\n const anchors = RUNG_ANCHOR[questionType]?.[rung];\n if (!anchors) return false;\n return anchors.Low === 0 && anchors.Typical === 0 && anchors.High === 0;\n}\n\n/**\n * The ceiling anchor for a (question type × rung) — the strongest band\n * (`High`). Used by the dashboard to label the ceiling on each sub-ladder and\n * by `/experiment-design` to size the next test against the question type's\n * ceiling.\n */\nexport function ceilingAnchor(\n questionType: QuestionType,\n rung: Rung,\n): number {\n return RUNG_ANCHOR[questionType]?.[rung]?.High ?? 0;\n}\n\n/**\n * The Risk threshold below which an assumption is \"validated enough\" for its\n * stage (DEV-5890). Pragmatic encroachment + Bezos two-way vs one-way doors;\n * Stage is the reversibility proxy. The threshold does NOT flip a status —\n * Live assumptions stay Live and ranked forever (`docs/validated.md`). It is\n * consumed by the dashboard's test-next surface, the `/assumptions audit`\n * skill, and the `/experiment-design` skill. It does NOT enter the\n * Confidence formula.\n *\n * The threshold is the max Risk you can have and still \"stop testing\" for\n * this stage. Later stages have LOWER thresholds because you need to drive\n * Risk down further (more evidence) before acting on a one-way door.\n * Discovery = 30: stop testing when Risk ≤ 30 (two-way door, little evidence\n * needed). Maturity = 5: stop testing when Risk ≤ 5 (one-way door, lots of\n * evidence needed). The bar being LOWER means the STANDARD is HIGHER — like\n * a high-jump bar: a lower number means you have to clear more.\n */\nexport const RISK_THRESHOLD_BY_STAGE: Record<Stage, number> = {\n Discovery: 30, // two-way door — act on weak evidence\n Validation: 15, // becoming one-way — need more before committing\n Scale: 10, // one-way door — strong evidence before scaling\n Maturity: 5, // defensive, often regulatory — strongest evidence\n};\n\n/**\n * The minimum Confidence floor for \"cleared\" (DEV-5890 fix). A belief with\n * Impact below the stage's Risk threshold could read Risk ≤ threshold with\n * ZERO evidence (Risk = Impact × (1 − 0/100) = Impact). Without a Confidence\n * floor, a low-Impact belief gets a free pass. The floor requires at least\n * some evidence before \"cleared\" is honest. Tightens with stage, mirroring\n * the Risk threshold.\n */\nexport const CONFIDENCE_FLOOR_BY_STAGE: Record<Stage, number> = {\n Discovery: 10, // any signal counts for a two-way door\n Validation: 25, // need a real reading, not just vibes\n Scale: 40, // need solid evidence before scaling\n Maturity: 60, // need strong evidence for a one-way door\n};\n\n/**\n * The Risk threshold for a given stage — the stopping rule for attention.\n * Falls back to the tightest threshold (Maturity) when the stage is absent,\n * so a missing stage never silently lowers the bar.\n */\nexport function riskThresholdForStage(stage: Stage | null | undefined): number {\n if (stage && stage in RISK_THRESHOLD_BY_STAGE) {\n return RISK_THRESHOLD_BY_STAGE[stage];\n }\n return RISK_THRESHOLD_BY_STAGE.Maturity;\n}\n\n/**\n * The minimum Confidence floor for a given stage. Falls back to the tightest\n * (Maturity) when absent.\n */\nexport function confidenceFloorForStage(stage: Stage | null | undefined): number {\n if (stage && stage in CONFIDENCE_FLOOR_BY_STAGE) {\n return CONFIDENCE_FLOOR_BY_STAGE[stage];\n }\n return CONFIDENCE_FLOOR_BY_STAGE.Maturity;\n}\n\n/**\n * Has the assumption cleared its stage's threshold? \"Cleared\" requires BOTH:\n * 1. Risk ≤ the stage's Risk threshold (enough evidence to drive Risk down)\n * 2. Confidence ≥ the stage's Confidence floor (at least some real evidence)\n *\n * The Confidence floor prevents a low-Impact belief from being \"cleared\" with\n * zero evidence — Risk = Impact × (1 − 0/100) = Impact, so a belief with\n * Impact below the threshold would read \"cleared\" without any readings. The\n * floor requires at least a minimum Confidence signal before \"cleared\" is\n * honest. \"Needs evidence\" = either condition fails — testing-priority.\n */\nexport function hasClearedThreshold(\n risk: number,\n stage: Stage | null | undefined,\n confidence?: number,\n): boolean {\n const riskCleared = risk <= riskThresholdForStage(stage);\n if (confidence === undefined) return riskCleared;\n const confCleared = confidence >= confidenceFloorForStage(stage);\n return riskCleared && confCleared;\n}","/**\n * Assumption completeness — a derived readiness meter, never stored.\n *\n * Replaces the old Gaps / presence-field machinery (OPS-1305): an assumption's\n * Draft-vs-Live readiness is the structural presence of its slots, not a set of\n * hand-maintained tags. `Completeness %` is `filled slots / all slots`; a\n * fully-filled assumption reads 100 and is Live-ready, an empty draft reads low.\n *\n * Slots (each an equal sixth):\n * Description · Lens · Impact · Scoring justification · Dependencies traced\n * · Question Type\n *\n * Question Type (DEV-5890) is the 6th slot: an assumption without a Question\n * Type has `Completeness %` < 100 and cannot go Live. The grill infers the\n * Question Type from the falsification test and confirms with the user\n * (the gaming guard).\n *\n * Pure, no backend dependency — the same function the recompute pass stamps\n * into the derived tuple and the audit checks readiness against.\n */\n\n/** The structural slots whose presence makes an assumption Live-ready. */\nexport const COMPLETENESS_SLOTS = [\n \"Description\",\n \"Lens\",\n \"Impact\",\n \"Scoring justification\",\n \"Dependencies traced\",\n \"Question Type\",\n] as const;\n\nexport type CompletenessSlot = (typeof COMPLETENESS_SLOTS)[number];\n\n/** The record shape completeness reads — an assumption's structural fields. */\nexport interface CompletenessInput {\n Description?: unknown;\n Lens?: unknown;\n Impact?: unknown;\n \"Scoring justification\"?: unknown;\n dependsOnIds?: unknown;\n enablesIds?: unknown;\n \"Question Type\"?: unknown;\n}\n\n/** A text slot is present only when it is a non-blank string. */\nfunction hasText(value: unknown): boolean {\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\n/** Impact is present when it is a real finite number (0 is a valid score). */\nfunction hasNumber(value: unknown): boolean {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\n/** Dependencies are traced once at least one Depends on / Enables link exists. */\nfunction hasAny(value: unknown): boolean {\n return Array.isArray(value) && value.length > 0;\n}\n\n/** Whether each slot is structurally present. */\nexport function completenessSlotPresence(\n record: CompletenessInput,\n): Record<CompletenessSlot, boolean> {\n return {\n Description: hasText(record.Description),\n Lens: hasText(record.Lens),\n Impact: hasNumber(record.Impact),\n \"Scoring justification\": hasText(record[\"Scoring justification\"]),\n \"Dependencies traced\":\n hasAny(record.dependsOnIds) || hasAny(record.enablesIds),\n \"Question Type\": hasText(record[\"Question Type\"]),\n };\n}\n\n/** The slots that are absent or blank on a record. */\nexport function missingCompletenessSlots(\n record: CompletenessInput,\n): CompletenessSlot[] {\n const present = completenessSlotPresence(record);\n return COMPLETENESS_SLOTS.filter((slot) => !present[slot]);\n}\n\n/** Completeness as a whole-number percentage (0, 20, 40, 60, 80, 100). */\nexport function assumptionCompleteness(record: CompletenessInput): number {\n const present = completenessSlotPresence(record);\n const filled = COMPLETENESS_SLOTS.filter((slot) => present[slot]).length;\n return Math.round((filled / COMPLETENESS_SLOTS.length) * 100);\n}\n\n/** True when every slot is present — the structural precondition to be Live. */\nexport function assumptionComplete(record: CompletenessInput): boolean {\n return missingCompletenessSlots(record).length === 0;\n}\n","/**\n * Strength — the signed reading value `s` the Confidence average reads.\n *\n * Formula (`ontology.yaml` → `derivations.strength`):\n * `RUNG_ANCHOR[questionType][rung][band] × sign(Result)`\n * — Validated positive, Invalidated negative; 0 unless Validated/Invalidated.\n *\n * The anchor lookup is 3D (DEV-5890): keyed by the linked assumption's\n * Question Type, the reading's Rung, and the row's Magnitude band. A rung\n * that is non-evidence for the assumption's question type carries anchor 0\n * across all bands, so the reading contributes `s=0` and is flagged at the\n * UI/skill layer (not a write blocker). `isNonEvidence(questionType, rung)`\n * is derived, never stored.\n *\n * Every rung carries a magnitude band (0.14); the lookup is the same for\n * testing and market rungs.\n */\nimport type { MagnitudeBand, QuestionType, Result, Rung } from \"../types.js\";\nimport { RUNG_ANCHOR } 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\n/** A reading counts toward Confidence only once concluded either way; an\n * Inconclusive reading carries no signal. The single definition the whole\n * derivation module (and its record→input mappers) share. */\nexport function isConcluded(result: Result): boolean {\n return result === \"Validated\" || result === \"Invalidated\";\n}\n\nexport interface StrengthInput {\n /** The linked assumption's question type — sets the anchor sub-ladder. */\n questionType: QuestionType;\n rung: Rung;\n result: Result;\n /** Magnitude band; defaults to \"Typical\" when absent. Applies to ALL rungs. */\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 const band = input.magnitudeBand ?? \"Typical\";\n return (RUNG_ANCHOR[input.questionType]?.[input.rung]?.[band] ?? 0) * s;\n}","/**\n * Question-type inference — the falsification-test rule (DEV-5890).\n *\n * The question type is set by what would prove the assumption WRONG, not by\n * what evidence is cheap to gather. \"Users will pay $50/mo\" is falsified by\n * offering it and watching them not pay → WillingnessToPay, regardless of how\n * many interview quotes exist. The function is conservative: if the\n * falsification test is ambiguous, it returns `Existence` (the most\n * permissive sub-ladder) and flags for human review. The migration script\n * runs this over all assumptions and produces a review queue for the ones it\n * isn't confident on.\n *\n * The grill enforces the gaming guard: the inferred type (from the\n * falsification bar) must match the stated type, or the assumption is rejected.\n * This is the rule that stops a team reframing \"will users pay?\" as \"do users\n * express willingness to pay?\" (existence question, qual ceiling) to avoid\n * running a market test.\n *\n * Rules (matched in order; first match wins):\n *\n * - Falsified by \"the regulation prohibits / the regulator rules against\n * / the compliance audit fails\" → Regulatory\n * - Falsified by \"they don't pay / don't sign up / don't commit / fake-door\n * rate below X%\" → WillingnessToPay\n * - Falsified by \"they stop using it / drop-off exceeds X / sustained usage\n * falls below / retention\" → ValueUtility\n * - Falsified by \"the treatment group doesn't differ from control / no\n * causal effect / variant doesn't outperform\" → CausalEffect\n * - Falsified by \"the rate is below X% / fewer than N of N / the proportion\n * is below / the share is below\" → Prevalence\n * - Falsified by \"they can't complete the flow / the system can't do X /\n * can't be built\" → Feasibility\n * - Falsified by \"no one reports this pain / no one describes this\n * mechanism / no one reports caring\" → Existence\n * - Otherwise (ambiguous) → Existence (flagged for human review)\n *\n * Regulatory is matched first because a regulator ruling against is a\n * distinctive falsification that shouldn't be misread as a willingness-to-pay\n * or feasibility claim. WillingnessToPay precedes ValueUtility because \"they\n * pay\" is the WTP falsification and \"they stop using\" is the ValueUtility\n * falsification — the keywords are distinct but WTP is the more specific claim\n * kind. CausalEffect precedes Prevalence because \"rate below X%\" appears in\n * both prevalence bars and A/B conversion-rate bars; the A/B framing\n * (\"treatment group\", \"control\", \"variant\", \"outperform\") is matched first.\n *\n * The function is a pure string-matching heuristic — no NLP, no external\n * calls. The migration's review queue is the backstop for cases the\n * heuristic misses.\n */\nimport type { QuestionType } from \"../types.js\";\n\n/** The default question type for an ambiguous falsification test — the most\n * permissive sub-ladder, and the migration's flagged-for-review default. */\nexport const DEFAULT_QUESTION_TYPE: QuestionType = \"Existence\";\n\ninterface Rule {\n readonly type: QuestionType;\n readonly patterns: readonly RegExp[];\n}\n\n// Lowercase match; word-boundary-tolerant. Patterns are intentionally specific\n// so a WTP bar (\"they don't pay\") isn't misread as Existence (\"no one reports\n// they pay\"). Order matters — see the module doc.\nconst RULES: readonly Rule[] = [\n {\n type: \"Regulatory\",\n patterns: [\n /\\bregulat(?:or|ion)\\s+(?:prohibits|rul(?:es|ed)\\s+against)\\b/,\n /\\bregulator\\b.*\\bagainst\\b/,\n /\\bcompliance\\s+audit\\s+fails?\\b/,\n /\\bprohibits?\\b.*\\bscoring\\b/,\n // Positive bar patterns (the register's rightIf text):\n /\\bregulat(?:or|ion)s?\\s+(?:approve|accept|confirm|permit)\\b/,\n /\\bfca\\b.*\\bconfirm\\b/,\n /\\blegal\\s+opinion\\b.*\\bstate/i,\n /\\bnon[-\\s]?compliant\\b.*\\boutputs?\\b/,\n /\\bconduct\\s+layer\\b.*\\b(?:safe|within|comply)\\b/,\n /\\bconsumer\\s+duty\\b/,\n /\\bguardrails?\\b.*\\b(?:regulat|comply|within)\\b/,\n /\\beducation\\b.*\\bguidance\\b.*\\bwithin\\b/,\n ],\n },\n {\n type: \"WillingnessToPay\",\n patterns: [\n /\\b(?:don't|do not|won't|will not)\\s+pay\\b/,\n /\\bfake[-\\s]?door\\s+(?:signup\\s+)?rate\\s+below\\b/,\n /\\bfake[-\\s]?door\\b.*\\brate\\b/,\n /\\bdon't\\s+sign\\s+up\\b/,\n /\\bno\\s+(?:buyer|user)s?\\s+(?:sign|pay|commit)\\b/,\n /\\bsigns?\\s+(?:an\\s+)?loi\\b/,\n /\\bput(?:s|ting)?\\s+down\\s+(?:a\\s+)?deposit\\b/,\n /\\bdon't\\s+commit\\b/,\n /\\boffered\\s+users?\\s+pay\\b/,\n /\\bpay(?:ing)?\\s+(?:rate|share)?\\s*(?:is\\s+)?below\\b/,\n /\\bfewer\\s+than\\s+\\d+\\s+of\\s+\\d+\\s+offered\\s+users?\\s+pay\\b/,\n // Positive bar patterns:\n /\\bsign\\s+(?:a\\s+)?(?:design[-\\s]?partner|pilot)\\s+agreement\\b/,\n /\\bsigned\\s+intent\\b/i,\n /\\bcommit(?:s|ting)?\\s+(?:before|to\\s+pay|to\\s+a)\\b/,\n /\\badopt\\s*(?:and\\s*)?pay\\b/,\n /\\bpay\\s+for\\b.*\\b(?:layer|harness|governed)\\b/,\n /\\bagree\\s+(?:in\\s+writing|to\\s+ingest|to\\s+sign)\\b/,\n /\\b(?:design|pilot)\\s+partner\\b.*\\bagreement\\b/,\n ],\n },\n {\n type: \"ValueUtility\",\n patterns: [\n /\\bstop\\s+using\\s+(?:it|the)\\b/,\n /\\bdrop[-\\s]?off\\s+exceeds\\b/,\n /\\bsustained\\s+usage\\s+falls?\\s+below\\b/,\n /\\bretention\\s+(?:drops?|falls?)\\s+below\\b/,\n /\\bweek-\\d+\\s+retention\\s+drops?\\s+below\\b/,\n /\\bdisuse\\b/,\n // Positive bar patterns:\n /\\bengage\\s+(?:turn[-\\s]by[-\\s]turn|with|positively)\\b/i,\n /\\bengage(?:s|ment)?\\s+with\\b/i,\n /\\breact\\s+positively\\b/i,\n /\\bcomfortable\\s+with\\b.*\\b(?:bank|storing|context)\\b/i,\n /\\bchoose\\s+(?:voice|as\\s+their\\s+primary)\\b/i,\n /\\bsustained\\s+(?:retention|usage|engagement)\\b/i,\n /\\bcome\\s+back\\b.*\\bsession/i,\n /\\bprefer\\b.*\\b(?:modality|interface|experience)\\b/i,\n ],\n },\n {\n type: \"CausalEffect\",\n patterns: [\n /\\btreatment\\s+group(?:'s)?\\s+doesn'?t\\s+differ\\s+from\\s+control\\b/,\n /\\bno\\s+causal\\s+effect\\s+of\\b/,\n /\\bvariant\\s+doesn'?t\\s+outperform\\s+control\\b/,\n /\\bdiffer\\s+from\\s+control\\b/,\n /\\boutperform\\s+control\\b/,\n /\\btreatment\\s+vs\\.?\\s+control\\b/,\n // Positive bar patterns:\n /\\bbeats?\\s+(?:a\\s+)?\\w+[-\\s]only\\s+(?:model|baseline|control)\\b/i,\n /\\brelative\\s+lift\\b/i,\n /\\breplicated\\s+in\\b.*\\bcohort/i,\n /\\bwins?\\s+≥?\\s*\\d+\\s*[×x]\\b/i,\n /\\bframing\\s+wins\\b/i,\n ],\n },\n {\n type: \"Prevalence\",\n patterns: [\n /\\brate\\s+is\\s+below\\b/,\n /\\bfewer\\s+than\\s+\\d+\\s+of\\s+\\d+\\s+(?:interviewed|surveyed|of)\\b/,\n /\\bfewer\\s+than\\s+\\d+%\\s+of\\s+\\w+\\s+hit\\b/,\n /\\bthe\\s+(?:rate|share|proportion)\\s+is\\s+below\\b/,\n /\\b(?:in\\s+)?surveyed\\s+teams\\b/,\n /\\bshare\\s+(?:of\\s+\\w+\\s+)?(?:is\\s+)?below\\b/,\n /\\bproportion\\s+is\\s+below\\b/,\n // Positive bar patterns:\n /\\b[≥>]\\s*\\d+%\\s+of\\b/i,\n /\\b[≥>]\\s*\\d+\\s+of\\b/i,\n /\\bnamed\\b.*\\b(?:as|in)\\s+a\\s+top[-\\s]\\d\\b/i,\n /\\b[≥>]\\s*\\d+\\s+(?:of\\s+\\d+\\s+)?(?:institutions?|consumers?|users?|buyers?|banks?|teams?)\\b/i,\n ],\n },\n {\n type: \"Feasibility\",\n patterns: [\n /\\bcan'?t\\s+complete\\s+the\\s+flow\\b/,\n /\\bsystem\\s+can'?t\\s+(?:do|ingest|handle|build)\\b/,\n /\\bcan'?t\\s+be\\s+built\\b/,\n /\\bcan'?t\\s+ingest\\b/,\n /\\bprototype\\s+can'?t\\s+be\\s+built\\b/,\n /\\bcan'?t\\s+\\w+\\s+(?:unaided|without\\s+(?:help|regressions))\\b/,\n // Positive bar patterns:\n /\\bcomplete\\s+the\\s+(?:flow|setup|wizard)\\b.*\\b(?:unaided|without\\s+help)\\b/i,\n /\\bingest\\b.*\\b(?:rows?|records?)\\b.*\\b(?:under|in)\\s+\\d+/i,\n /\\b(?:can|able\\s+to)\\s+(?:build|ship|deliver)\\b.*\\b(?:without|no)\\s+(?:regress|break)/i,\n ],\n },\n {\n type: \"Existence\",\n patterns: [\n /\\bno\\s+one\\s+(?:we\\s+interview\\s+)?(?:describes|reports|mentions)\\b/,\n /\\bno\\s+interviewee(?:s)?\\s+(?:describe|report|mention)s?\\b/,\n /\\bcan'?t\\s+describe\\s+the\\s+mechanism\\b/,\n /\\bno\\s+one\\s+reports\\s+caring\\b/,\n /\\bno\\s+one\\s+describes\\b/,\n // Positive bar patterns:\n /\\bindependently\\s+states?\\b.*\\b(?:needs?|wants?|values?|sees)\\b/i,\n /\\bindependently\\s+(?:report|state|confirm)s?\\b/i,\n /\\bsay\\s+they'?d\\s+(?:be\\s+)?(?:comfortable|prefer)\\b/i,\n /\\b(?:credible|independent)\\b.*\\b(?:states?|reports?|confirms?)\\b/i,\n ],\n },\n];\n\n/** Normalize a string for matching: lowercase, collapse whitespace. */\nfunction normalize(s: string): string {\n return s.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\n\n/**\n * Infer the Question Type from an assumption's description and falsification\n * bar. The falsification bar (`wrongIfBar`) carries the signal — the\n * description is only consulted as a fallback context. Returns the most\n * permissive type (`Existence`) for an ambiguous bar; the migration's review\n * queue is the backstop.\n */\nexport function inferQuestionType(\n description: string,\n wrongIfBar: string,\n): QuestionType {\n const bar = normalize(wrongIfBar ?? \"\");\n const desc = normalize(description ?? \"\");\n const hay = bar.length > 0 ? `${bar} ${desc}` : desc;\n\n for (const rule of RULES) {\n for (const re of rule.patterns) {\n if (re.test(hay)) return rule.type;\n }\n }\n return DEFAULT_QUESTION_TYPE;\n}\n\n/** The set of question types the inference rule can return. Exported for\n * the migration's \"covered types\" assertion. */\nexport const INFERABLE_QUESTION_TYPES: readonly QuestionType[] = [\n \"Existence\",\n \"Prevalence\",\n \"CausalEffect\",\n \"WillingnessToPay\",\n \"ValueUtility\",\n \"Regulatory\",\n \"Feasibility\",\n] as const;\n\n/** Whether an inferred question type should be flagged for human review.\n * Conservative: only the confident matches are clean; the default\n * (Existence-from-ambiguous) is always flagged. A match is \"confident\" if the\n * bar matched a rule other than the Existence fallback. */\nexport function needsReview(\n description: string,\n wrongIfBar: string,\n inferred: QuestionType,\n): boolean {\n if (wrongIfBar == null || wrongIfBar.trim() === \"\") return true;\n // Re-run the inference; if the bar was empty/ambiguous (no rule matched the\n // bar text), the inferred type is the fallback and should be reviewed.\n const bar = normalize(wrongIfBar);\n for (const rule of RULES) {\n if (rule.type === inferred) {\n for (const re of rule.patterns) {\n if (re.test(bar)) return false;\n }\n }\n }\n return true;\n}","/**\n * Migration entry point — DEV-5890.\n *\n * Takes the OLD-shape register (assumptions without `Question Type`, readings\n * with stale Strength) and produces the new shape: each assumption with\n * `Question Type` filled via `inferQuestionType`, each reading's `s`\n * recomputed via the 3D `RUNG_ANCHOR[questionType][rung][band]` lookup, each\n * reading flagged if its rung is non-evidence for the linked assumption's\n * question type (keeping its would-have-been `s` visible for diffing), and a\n * summary report of Confidence deltas + flag counts.\n *\n * The migration is reversible: the entry point writes a backup of the\n * pre-migration state to `doshi-validation-os/docs/migration/backups/`. Roll\n * back restores from the backup.\n *\n * Pure — no I/O. The caller (the migration script) reads the register, calls\n * `migrateRegister`, and writes the result. Tested at the highest seam with a\n * fixture covering existence, WTP, causal, and regulatory cases.\n */\nimport {\n confidence,\n inferQuestionType,\n isNonEvidence,\n needsReview,\n readingStrength,\n RISK_THRESHOLD_BY_STAGE,\n riskThresholdForStage,\n RUNG_ANCHOR,\n type ConfidenceReadingInput,\n} from \"./index.js\";\nimport type {\n AnyRecord,\n AssumptionRecord,\n QuestionType,\n ReadingRecord,\n Result,\n Rung,\n} from \"../types.js\";\n\n/** A minimal assumption shape for migration input. */\nexport interface MigrationAssumption {\n id: string;\n Description?: string;\n /** The falsification test — the \"we're wrong if\" bar from the grill. */\n wrongIfBar?: string;\n /** The existing question type, if any (otherwise inferred). */\n \"Question Type\"?: QuestionType | null;\n /** Existing derived Confidence (for the delta report). */\n derived?: { confidence?: number } | null;\n /** The assumption's Stage, for the threshold comparison in the summary. */\n Stage?: string | null;\n Impact?: number | null;\n moot?: boolean;\n dependsOnIds?: string[];\n enablesIds?: string[];\n}\n\n/** A minimal reading shape for migration input. */\nexport interface MigrationReading {\n id: string;\n /** The assumption id this reading's belief scores (single-belief migration). */\n assumptionId: string;\n Rung: Rung;\n Result: Result;\n magnitudeBand?: \"Low\" | \"Typical\" | \"High\" | null;\n Source: string | null;\n Representativeness: number;\n Credibility: number;\n Date: string | null;\n experimentId: string | null;\n /** Existing derived Strength (for the delta report). */\n derived?: { strength?: number } | null;\n}\n\n/** The output shape for a migrated assumption. */\nexport interface MigratedAssumption extends MigrationAssumption {\n \"Question Type\": QuestionType;\n /** Whether the inferred question type was flagged for human review. */\n questionTypeReviewNeeded: boolean;\n /** The new derived Confidence (recomputed under the new sub-ladder). */\n newConfidence: number;\n /** The pre-migration Confidence (for the delta report). */\n oldConfidence: number;\n /** The Confidence delta (new − old). */\n confidenceDelta: number;\n /** The stage's Risk threshold (for the summary). */\n riskThreshold: number;\n}\n\n/** The output shape for a migrated reading's belief. */\nexport interface MigratedReading {\n id: string;\n assumptionId: string;\n Rung: Rung;\n /** The recomputed Strength (s) under the new sub-ladder. */\n newStrength: number;\n /** The pre-migration Strength (for diffing). */\n oldStrength: number;\n /** True if the rung is non-evidence for the linked assumption's question type. */\n nonEvidence: boolean;\n}\n\n/** The full migration result. */\nexport interface MigrationResult {\n assumptions: MigratedAssumption[];\n readings: MigratedReading[];\n /** The number of readings flagged as non-evidence. */\n nonEvidenceFlagCount: number;\n /** The number of assumptions flagged for human review of their question type. */\n reviewQueueCount: number;\n /** Per-assumption Confidence deltas, sorted by |delta| descending. */\n confidenceDeltas: { id: string; delta: number; oldConfidence: number; newConfidence: number }[];\n /** Per-assumption ranking shifts in the Risk-ranked register (oldRank →\n * newRank, 1-indexed; positive = moved down the ranking, negative = moved\n * up). Sorted by |shift| descending. */\n rankingShifts: { id: string; oldRank: number; newRank: number; shift: number }[];\n}\n\n/**\n * Migrate an old-shape register to the new question-type-aware shape. Pure —\n * no I/O. The caller reads the register, calls this, and writes the result;\n * the backup is the caller's responsibility (write a dated copy of the input\n * before calling `migrateRegister`).\n *\n * The migration:\n * 1. Infers `Question Type` for each assumption from its falsification bar.\n * 2. Recomputes each reading's `s` via `RUNG_ANCHOR[questionType][rung][band]`.\n * 3. Flags readings whose rung is non-evidence for the linked assumption's\n * question type (keeps the would-have-been `s` visible for diffing).\n * 4. Recomputes each assumption's Confidence under the new sub-ladder.\n * 5. Produces a summary report of Confidence deltas and flag counts.\n */\nexport function migrateRegister(\n assumptions: MigrationAssumption[],\n readings: MigrationReading[],\n): MigrationResult {\n // 1. Infer Question Type for each assumption; build the id → type map.\n const questionTypeById = new Map<string, QuestionType>();\n const reviewNeededById = new Set<string>();\n for (const a of assumptions) {\n const existing = a[\"Question Type\"];\n let qt: QuestionType;\n let review = false;\n if (existing && ([\"Existence\", \"Prevalence\", \"CausalEffect\", \"WillingnessToPay\", \"ValueUtility\", \"Regulatory\", \"Feasibility\"] as const).includes(existing)) {\n // An existing valid question type is kept; still check whether the bar\n // matches (the gaming guard — infer from the bar and flag a mismatch).\n qt = existing;\n // No review when the question type was explicitly set.\n } else {\n qt = inferQuestionType(a.Description ?? \"\", a.wrongIfBar ?? \"\");\n review = needsReview(a.Description ?? \"\", a.wrongIfBar ?? \"\", qt);\n if (review) reviewNeededById.add(a.id);\n }\n questionTypeById.set(a.id, qt);\n }\n\n // 2. Recompute each reading's Strength under the new sub-ladder.\n const migratedReadings: MigratedReading[] = [];\n let nonEvidenceFlagCount = 0;\n for (const r of readings) {\n const questionType = questionTypeById.get(r.assumptionId) ?? \"Existence\";\n const band = r.magnitudeBand ?? \"Typical\";\n const newStrength = readingStrength({\n questionType,\n rung: r.Rung,\n result: r.Result,\n magnitudeBand: band,\n });\n // The would-have-been Strength under the OLD single-ladder (DEV-5880 /\n // pre-DEV-5890 anchors). We approximate by the legacy anchor for the rung\n // (the pre-DEV-5890 single-ladder Typical values) × sign. This is a\n // best-effort diff; the caller can supply the actual old Strength via\n // `r.derived.strength` when available.\n const oldStrength = r.derived?.strength ?? 0;\n const nonEvidence = isNonEvidence(questionType, r.Rung);\n if (nonEvidence) nonEvidenceFlagCount += 1;\n migratedReadings.push({\n id: r.id,\n assumptionId: r.assumptionId,\n Rung: r.Rung,\n newStrength,\n oldStrength,\n nonEvidence,\n });\n }\n\n // 3. Recompute each assumption's Confidence under the new sub-ladder.\n // Group the readings into ConfidenceReadingInput by assumption, threading\n // the inferred question type.\n const inputsByAssumption = new Map<string, ConfidenceReadingInput[]>();\n for (const a of assumptions) inputsByAssumption.set(a.id, []);\n for (const r of readings) {\n const questionType = questionTypeById.get(r.assumptionId) ?? \"Existence\";\n inputsByAssumption.get(r.assumptionId)?.push({\n id: r.id,\n source: r.Source,\n rung: r.Rung,\n result: r.Result,\n questionType,\n representativeness: r.Representativeness,\n credibility: r.Credibility,\n date: r.Date,\n magnitudeBand: r.magnitudeBand ?? undefined,\n experimentId: r.experimentId,\n });\n }\n\n const migratedAssumptions: MigratedAssumption[] = assumptions.map((a) => {\n const questionType = questionTypeById.get(a.id) ?? \"Existence\";\n const oldConfidence = a.derived?.confidence ?? 0;\n const newConfidence = confidence(inputsByAssumption.get(a.id) ?? []);\n const stage = (a.Stage as keyof typeof RISK_THRESHOLD_BY_STAGE | null) ?? null;\n return {\n ...a,\n \"Question Type\": questionType,\n questionTypeReviewNeeded: reviewNeededById.has(a.id),\n newConfidence,\n oldConfidence,\n confidenceDelta: Math.round((newConfidence - oldConfidence) * 100) / 100,\n riskThreshold: riskThresholdForStage(stage as never),\n };\n });\n\n const confidenceDeltas = migratedAssumptions\n .map((a) => ({\n id: a.id,\n delta: a.confidenceDelta,\n oldConfidence: a.oldConfidence,\n newConfidence: a.newConfidence,\n }))\n .sort((a, b) => Math.abs(b.delta) - Math.abs(a.delta));\n\n // Ranking shift: rank assumptions by (oldConfidence desc, id) and\n // (newConfidence desc, id), then compute the rank delta per assumption.\n // 1-indexed; positive shift = moved DOWN the ranking (less confident),\n // negative = moved UP (more confident).\n const oldRanked = [...assumptions]\n .map((a) => ({ id: a.id, c: a.derived?.confidence ?? 0 }))\n .sort((a, b) => b.c - a.c || a.id.localeCompare(b.id));\n const newRanked = [...migratedAssumptions]\n .map((a) => ({ id: a.id, c: a.newConfidence }))\n .sort((a, b) => b.c - a.c || a.id.localeCompare(b.id));\n const oldRank = new Map(oldRanked.map((r, i) => [r.id, i + 1]));\n const newRank = new Map(newRanked.map((r, i) => [r.id, i + 1]));\n const rankingShifts = migratedAssumptions\n .map((a) => {\n const o = oldRank.get(a.id) ?? 0;\n const n = newRank.get(a.id) ?? 0;\n return { id: a.id, oldRank: o, newRank: n, shift: n - o };\n })\n .filter((r) => r.oldRank > 0 && r.newRank > 0)\n .sort((a, b) => Math.abs(b.shift) - Math.abs(a.shift));\n\n return {\n assumptions: migratedAssumptions,\n readings: migratedReadings,\n nonEvidenceFlagCount,\n reviewQueueCount: reviewNeededById.size,\n confidenceDeltas,\n rankingShifts,\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 * 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/**\n * The five registers. `goals` was unified into `experiments` and the `people`\n * reference collection retired (OPS-1305): Owner / Agreed by now reference a\n * dashboard user (the auth-sourced team list), not a register row.\n */\nexport const REGISTERS = [\n \"assumptions\",\n \"experiments\",\n \"readings\",\n \"decisions\",\n \"glossary\",\n] as const;\nexport type Register = (typeof REGISTERS)[number];\n\n/** There is no reference collection any more — a collection is a register. */\nexport type Collection = Register;\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\";\n/**\n * The unified evidence-plan lifecycle (OPS-1305). `Draft` is the new gate a\n * commit clears (Draft→Running); conclude+verdict stays the Running→Closed\n * gate. Absorbs what the retired Goal record's status used to carry.\n */\nexport type ExperimentStatus = \"Draft\" | \"Running\" | \"Closed\" | \"Archived\";\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/**\n * The question-type-aware evidence ladder (DEV-5890). A rung is an evidence\n * TYPE; magnitude band (Low/Typical/High) is the intensity *within* a type.\n * The band applies to EVERY rung, so every rung looks up its anchor through\n * `RUNG_ANCHOR[questionType][rung][band]` — the anchor (ceiling `s`) is per\n * (question type × rung × band). A rung that is **non-evidence** for a\n * question type carries anchor 0 across all bands (contributes `s=0`,\n * flagged at the UI/skill layer — not a write blocker).\n *\n * Talk: 3 / 6 / 10 (Opinion / Pitch-deck / Anecdotal merged)\n * Desk research: 15 / 15 / 15 (flat — desk research has no meaningful bands,\n * but the field exists for uniformity)\n * Signed up: 30 / 50 / 70 (consumer lens's first do-rung)\n * Observed usage: 30 / 50 / 70 (consumer lens; was Prototype usage + Survey\n * at scale, now collapsed)\n * Signed intent: 30 / 50 / 70 (commercial/investor lens)\n * Paying users: 30 / 50 / 70 (commercial/investor lens)\n *\n * The above anchors are the LEGACY single-ladder values; the live anchors are\n * per question type — see `derivation/rung.ts` for the 3D table. The rung\n * vocabulary itself is fixed across all question types; only the anchors\n * (including `0` for non-evidence) vary.\n *\n * The lens determines which \"do\" rungs are available; Talk + Desk work for any\n * lens. The rung-to-lens mapping is a grading guideline, not a schema\n * constraint — any Rung can appear on any assumption.\n */\nexport const TESTING_RUNGS = [\n \"Talk\",\n \"Desk research\",\n \"Signed up\",\n \"Observed usage\",\n] as const;\nexport const MARKET_RUNG_VALUES = [\"Signed intent\", \"Paying users\"] as const;\nexport type TestingRung = (typeof TESTING_RUNGS)[number];\nexport type MarketRung = (typeof MARKET_RUNG_VALUES)[number];\nexport type Rung = TestingRung | MarketRung;\n\n/**\n * The four discovery stages (docs/stage-policy.md). The stored value is the\n * name; the ordinal 1–4 is for sorting only. Stage sets the Risk threshold\n * for action (the stopping rule, `RISK_THRESHOLD_BY_STAGE`) — it does NOT\n * enter the Confidence formula. Orthogonal to Question Type (kind of claim)\n * and Lens (who the actor is).\n */\nexport const STAGES = [\"Discovery\", \"Validation\", \"Scale\", \"Maturity\"] as const;\nexport type Stage = (typeof STAGES)[number];\n\n/**\n * The seven question types (docs/question-types.md). The kind of CLAIM an\n * assumption raises — set by the falsification-test rule (what would prove\n * the assumption wrong), not by what evidence is cheap. The question type\n * determines which rungs are probative, what the ceiling is, and which rungs\n * are non-evidence (anchor 0) — via `RUNG_ANCHOR[questionType][rung][band]`.\n * Orthogonal to Stage (kind of response) and Lens (who the actor is).\n */\nexport const QUESTION_TYPES = [\n \"Existence\",\n \"Prevalence\",\n \"CausalEffect\",\n \"WillingnessToPay\",\n \"ValueUtility\",\n \"Regulatory\",\n \"Feasibility\",\n] as const;\nexport type QuestionType = (typeof QUESTION_TYPES)[number];\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 derived numbers stored on an assumption (never hand-typed). */\nexport interface AssumptionDerived {\n derivedImpact: number;\n risk: number;\n confidence: number;\n /** Structural readiness meter, 0–100 (see `derivation/completeness.ts`). */\n completeness: number;\n}\n\nexport interface AssumptionRecord extends BaseRecord {\n Title: string;\n Description: string;\n Lens: string | null;\n /** The kind of external-actor response this belief tests (Discovery /\n * Validation / Scale / Maturity). Sets the Risk threshold for action. */\n Stage: Stage | null;\n /** The kind of claim this belief raises (Existence / Prevalence / ...).\n * Determines which rungs are probative, the ceiling, and non-evidence —\n * via `RUNG_ANCHOR[questionType][rung][band]`. */\n \"Question Type\": QuestionType | 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 /** Reference to a dashboard user (auth team list), not a `people` row. */\n Owner: string[];\n moot: boolean;\n /**\n * Kept, narrowed to the Impact-seed rationale (OPS-1305): why the seed\n * `Impact` was scored as it was, incl. dated moot lines. The `5 Whys`,\n * `Metric for truth`, and `Gaps` fields are gone — the why-trace lives in the\n * `Depends on / Enables` chain and the audit check-types are transient grill\n * stages, not stored tags. Readiness is the derived `completeness`.\n */\n \"Scoring justification\": string;\n dependsOnIds: string[];\n enablesIds: string[];\n contradictsIds: string[];\n readingIds: string[];\n derived: AssumptionDerived;\n}\n\n/**\n * One belief's scoring inside a reading — the per-assumption verdict an\n * artifact carries. A reading (one artifact ROW) may score several beliefs at\n * once. The Rung is a property of the artifact, so it (and the market magnitude\n * band) lives on the row, ONE per artifact (OPS 0.10): a reading is at a single\n * evidence rung and reads for/against each belief at that rung. Only the Result\n * and its rationale vary per belief. `strength` is derived (row rung × sign of\n * this belief's Result), never hand-typed.\n */\nexport interface BeliefScore {\n assumptionId: string;\n Result: Result;\n /** The rationale for this belief's Result at the row's rung. */\n \"Grading justification\": string;\n /** The verbatim quote or observed fact this belief was graded from. */\n excerpt?: string;\n /** Derived per belief: row rung anchor × sign(Result) [× row magnitude band]. */\n derived: { strength: number };\n /** Provenance: the original reading id this belief was migrated from. */\n sourceReadingId?: string;\n /** Optional review caveat, e.g. a second-hand-credibility note. */\n reviewNote?: string;\n}\n\nexport interface ReadingRecord extends BaseRecord {\n Title: string;\n /** The independence/dedupe key — the generator (person / dataset / cohort). */\n Source: string | null;\n /**\n * Provenance links (recording, dashboard, CRM row, user id) — 0..N, drives no\n * math and never keys dedupe (OPS-1305). Split out from `Source` so the\n * dedupe key stays narrow.\n */\n contextLinks: string[];\n /** The originating plan, or null for a bare/found reading (no Goal origin). */\n experimentId: string | null;\n /** The evidence rung — one per artifact; every belief is scored at it (OPS 0.10). */\n Rung: Rung;\n /** For a Market-rung reading: the magnitude band from the absolute outcome. */\n magnitudeBand?: MagnitudeBand;\n Representativeness: SourceQualityPick;\n Credibility: SourceQualityPick;\n Date: string | null;\n Owner: string[];\n /** Free-text narrative of the reading. */\n body?: string;\n /** Per-belief scores — one artifact row can score several beliefs at once. */\n beliefs: BeliefScore[];\n /** Convenience projection of `beliefs[].assumptionId`; kept in sync on write. */\n assumptionIds: string[];\n derived: { sourceQuality: number };\n}\n\n/**\n * A pre-registered bar line — the per-belief pass/fail test on an experiment,\n * stored as an embedded array on the experiment (no identity of its own; see\n * `connectors/nosql-schema.md`). `barVerdict` is set once at closure and is a\n * report only — never folded into Confidence.\n */\nexport interface BarLine {\n assumptionId: string;\n rightIf: string;\n wrongIf?: string | null;\n plannedRung: Rung;\n barVerdict?: Result | null;\n}\n\n/**\n * The derived numbers stored on an experiment (never hand-typed).\n * `experimentConfidence` is the [0, 100] confidence gauge (50 = neutral),\n * coverage-gated and verdict-aligned; see `derivation/experiment-confidence.ts`.\n */\nexport interface ExperimentDerived {\n experimentConfidence: number;\n}\n\nexport interface ExperimentRecord extends BaseRecord {\n Title: string;\n Instrument: string | null;\n Feasibility: Feasibility | null;\n Status: ExperimentStatus;\n /** Free-text narrative of the plan. */\n body?: string;\n closureReason: \"Completed\" | \"Early-stop\" | \"Kill\" | null;\n /** Optional deadline a committed plan carries (folded in from the Goal). */\n Deadline: string | null;\n /** Terminal closure outcome, null until Closed (folded in from the Goal). */\n Outcome: \"Achieved\" | \"Missed\" | \"Dropped\" | null;\n /** Reference to a dashboard user, not a `people` row. */\n Owner: string[];\n Date: string | null;\n /** The embedded pre-registered bar lines; drives progress-to-conclusion. */\n barLines: BarLine[];\n /** Convenience projection: the assumptions the bar lines test. */\n barLineAssumptionIds: string[];\n /** Derived numbers — recomputed on every touching write. */\n derived?: ExperimentDerived;\n}\n\nexport interface DecisionRecord extends BaseRecord {\n Title: string;\n Status: DecisionStatus;\n /** The one-line statement of what was decided (promoted from the body). */\n Statement: string;\n /** Why the Unanimity score was scored as it was (promoted from the body). */\n \"Unanimity justification\": string;\n /** References to dashboard users (auth team list), not `people` rows. */\n Owner: string[];\n \"Agreed by\": string[];\n basedOnIds: string[];\n resolvesIds: string[];\n}\n\n/** One structured \"don't say\" entry on a glossary term. */\nexport interface GlossaryAvoid {\n audience: string;\n phrase: string;\n fix: string;\n}\n\nexport interface GlossaryRecord extends BaseRecord {\n Title: string;\n Status: GlossaryStatus;\n /** All properties, no body (OPS-1305): the terminology check parses these. */\n Definition: string;\n Avoid: GlossaryAvoid[];\n \"How it differs\": string;\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 | \"decision-based-on\"\n | \"decision-resolves\";\n","/**\n * Confidence — signed −100…100, 0 = no evidence.\n *\n * Formula (`ontology.yaml` → `derivations.confidence`):\n * (Σ wi·si) / (Σ_rung W0[rung] + Σ wi), per-rung W0,\n * wi = |si| × Source quality × commitment, si = the reading's signed Strength,\n * commitment = 1.0 for an experiment-linked reading, 0.85 for a found one.\n *\n * W0 is per-rung: each rung has its own prior weight controlling how many\n * distinct sources it takes to approach that rung's anchor (cap). Desk\n * research has a low W0 (2 — one authoritative source nearly saturates);\n * talk rungs have a higher W0 (6.5 — needs ~10 readings to approach the\n * cap); do-rungs (Survey/Prototype/Signed/Paying) have high W0s (~120-410\n * — needs ~20 readings to reach 75% of cap). When readings span multiple\n * rungs, each rung with evidence contributes its W0 to the denominator\n * once (one prior per evidence stream).\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). Market-rung readings never dedupe (each closed commitment\n * is its own unit). No corroboration bump.\n */\nimport type { MagnitudeBand, QuestionType, Result, Rung } from \"../types.js\";\nimport { MARKET_RUNG_VALUES } from \"../types.js\";\nimport { round2 } from \"./round.js\";\nimport { sourceQuality } from \"./source-quality.js\";\nimport { isConcluded, readingStrength } from \"./strength.js\";\n\n/** Market rungs never dedupe (each closed commitment is its own unit). */\nconst MARKET_RUNG_SET = new Set<Rung>(MARKET_RUNG_VALUES);\nfunction isMarketRung(rung: Rung): boolean {\n return MARKET_RUNG_SET.has(rung);\n}\n\n/**\n * Per-rung prior weight — controls how many distinct sources approach the\n * rung's anchor. Tuned so: Desk 2 readings → ~90% of cap; talk 10 readings →\n * ~90% of cap; do-rungs 20 readings → ~75% of cap. See\n * `docs/evidence-ladder.md` for the derivation.\n */\nexport const W0_BY_RUNG: Record<Rung, number> = {\n // Talk — 10 readings → ~90% of cap.\n Talk: 6.5,\n // Desk research — 2 readings → ~90% of cap (authoritative, rare).\n \"Desk research\": 2,\n // All do-rungs — 20 readings → ~75% of cap. Equal ceilings (70) and equal\n // W0s across consumer (Signed up / Observed usage) and commercial\n // (Signed intent / Paying users) lenses.\n \"Signed up\": 327,\n \"Observed usage\": 327,\n \"Signed intent\": 327,\n \"Paying users\": 327,\n};\n\n/**\n * The neutral prior weight for a rung — how much evidence at this rung is\n * needed to overcome the prior. Per-rung (not a flat constant): see\n * {@link W0_BY_RUNG}.\n */\nexport function w0ForRung(rung: Rung): number {\n return W0_BY_RUNG[rung] ?? 100;\n}\n\n/**\n * The legacy flat W0 — retained for backwards compatibility only. New code\n * should use {@link w0ForRung}. Equal to the old default of 100.\n * @deprecated Use {@link w0ForRung} for per-rung priors.\n */\nexport const W0 = 100;\n\n/**\n * Commitment factor for a *found* reading — one with no originating experiment.\n * A pre-registered (experiment-linked) reading weighs at full commitment (1.0);\n * a found reading is discounted to this. It is a SMALL tiebreaker: it scales the\n * weight only, never the Strength, so it can never reorder readings across rungs\n * (\"Rung dominates\").\n */\nexport const COMMITMENT_FOUND = 0.85;\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 /** The linked assumption's question type — sets the anchor sub-ladder. */\n questionType: QuestionType;\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 * The originating experiment, or null/undefined for a *found* reading. Drives\n * the commitment factor in the weight (found → {@link COMMITMENT_FOUND}).\n */\n experimentId?: string | null;\n}\n\n/** The commitment weighting for a reading: full for committed, discounted for found. */\nexport function commitmentFactor(experimentId: string | null | undefined): number {\n return experimentId ? 1.0 : COMMITMENT_FOUND;\n}\n\nexport interface Scored {\n input: ConfidenceReadingInput;\n strength: number;\n sq: number;\n /** The reading's weight in the average: |strength| × Source quality × commitment. */\n weight: number;\n}\n\n/**\n * Score every concluded reading and resolve the Source dedupe — the shared\n * front half of the Confidence average. `confidence()` reduces the winners to\n * a number; `confidenceAttribution()` reuses the same winners so the movers it\n * reports always decompose the very number the drawer shows. Market rungs never\n * dedupe (each closed commitment is its own unit).\n */\nexport function scoreAndDedupe(readings: ConfidenceReadingInput[]): Scored[] {\n const scored: Scored[] = readings\n .filter((r) => isConcluded(r.result))\n .map((r) => {\n const strength = readingStrength(r);\n const sq = sourceQuality(r.representativeness, r.credibility);\n const weight = Math.abs(strength) * sq * commitmentFactor(r.experimentId);\n return { input: r, strength, sq, weight };\n })\n .filter((x) => x.strength !== 0);\n\n const best = new Map<string, Scored>();\n for (const x of scored) {\n if (isMarketRung(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 return [...best.values()];\n}\n\nexport function confidence(readings: ConfidenceReadingInput[]): number {\n const winners = scoreAndDedupe(readings);\n // Per-rung prior: sum W0 once for each rung that has ≥1 concluded reading.\n // This preserves each rung's accumulation behaviour independently — desk\n // stays fast (low W0), talk stays slow (high W0) — and mixed-rung evidence\n // gets each rung's prior added once (one prior per evidence stream).\n const rungsPresent = new Set(winners.map((x) => x.input.rung));\n let den = 0;\n for (const rung of rungsPresent) den += w0ForRung(rung);\n let num = 0;\n for (const x of winners) {\n num += x.weight * x.strength;\n den += x.weight;\n }\n return den > 0 ? round2(num / den) : 0;\n}\n","/**\n * Confidence attribution — the \"what's moving the number\" half of the\n * understanding layer (OPS-1276). Decomposes an assumption's Confidence into\n * the experiments (and direct readings) contributing to it, ranked by how hard\n * each pushes the number up or down.\n *\n * A winner's contribution is its signed share of the average:\n * cᵢ = (weightᵢ · strengthᵢ) / den,\n * den = Σ_rung W0[rung] + Σ weight (one prior per rung with evidence)\n * so Σ contributions = Σ(wᵢ·sᵢ)/den = Confidence (the W0·0 prior term is 0).\n * The reveal therefore literally adds up to the hero number. Contributions are\n * grouped by the reading's experiment (experiment-less readings — bare/found —\n * fall into a \"direct\" bucket), then ranked by |contribution|. A reading's\n * origin is an experiment or nothing; the retired Goal container is gone\n * (OPS-1305).\n */\nimport { w0ForRung, scoreAndDedupe, type ConfidenceReadingInput } from \"./confidence.js\";\nimport { round2 } from \"./round.js\";\n\n/**\n * The reading shape the attribution reveal consumes. `experimentId` (the\n * grouping key, and the commitment-factor driver) lives on\n * {@link ConfidenceReadingInput}; this alias marks the attribution call sites.\n */\nexport type AttributionReadingInput = ConfidenceReadingInput;\n\n/** What a mover is anchored to — an experiment, or nothing (direct). */\nexport type MoverKind = \"experiment\" | \"direct\";\n\nexport interface Mover {\n /** Stable grouping key: the experiment id, or \"direct\". */\n key: string;\n kind: MoverKind;\n /** The experiment id when `kind === \"experiment\"`, else null. */\n experimentId: string | null;\n /** Signed push on Confidence; the whole set sums to `confidence`. */\n contribution: number;\n /** |contribution| — the rank key and the \"how hard\" magnitude. */\n magnitude: number;\n /** How many (deduped) readings back this mover. */\n readingCount: number;\n /** The winning readings' ids, for drill-through. */\n readingIds: string[];\n}\n\nexport interface Attribution {\n /** The same Confidence the derived box shows (from the same winners). */\n confidence: number;\n /** Movers ranked by |contribution|, strongest first. */\n movers: Mover[];\n}\n\nfunction bucketOf(r: AttributionReadingInput): {\n key: string;\n kind: MoverKind;\n experimentId: string | null;\n} {\n if (r.experimentId) {\n return {\n key: r.experimentId,\n kind: \"experiment\",\n experimentId: r.experimentId,\n };\n }\n return { key: \"direct\", kind: \"direct\", experimentId: null };\n}\n\nexport function confidenceAttribution(\n readings: AttributionReadingInput[],\n): Attribution {\n const winners = scoreAndDedupe(readings);\n // Per-rung prior: sum W0 once for each rung with ≥1 concluded reading,\n // matching the `confidence()` formula.\n const rungsPresent = new Set(winners.map((x) => x.input.rung));\n let den = 0;\n for (const rung of rungsPresent) den += w0ForRung(rung);\n for (const x of winners) den += x.weight;\n\n const byKey = new Map<string, Mover>();\n let num = 0;\n for (const x of winners) {\n num += x.weight * x.strength;\n const b = bucketOf(x.input as AttributionReadingInput);\n const contribution = (x.weight * x.strength) / den;\n const cur = byKey.get(b.key);\n if (cur) {\n cur.contribution += contribution;\n cur.readingCount += 1;\n cur.readingIds.push(x.input.id);\n } else {\n byKey.set(b.key, {\n key: b.key,\n kind: b.kind,\n experimentId: b.experimentId,\n contribution,\n magnitude: 0, // finalised below\n readingCount: 1,\n readingIds: [x.input.id],\n });\n }\n }\n\n const movers = [...byKey.values()].map((m) => ({\n ...m,\n contribution: round2(m.contribution),\n magnitude: round2(Math.abs(m.contribution)),\n }));\n movers.sort((a, b) => b.magnitude - a.magnitude);\n\n return {\n confidence: den > 0 ? round2(num / den) : 0,\n movers,\n };\n}\n","/**\n * Progress-to-conclusion — how close a running experiment is to concluding\n * (OPS-1276). An experiment concludes when every pre-registered bar line has\n * been given a Bar verdict; progress is the count of settled bars against the\n * total. Bar verdict is a report, never a Confidence input (nosql-schema.md),\n * so reading it here is exactly its intended use.\n */\nexport interface BarLineInput {\n /** null/\"\" until the bar is judged at closure. */\n barVerdict?: string | null;\n}\n\nexport interface Progress {\n /** Pre-registered bar lines on the experiment. */\n total: number;\n /** Bars that have a verdict. */\n settled: number;\n /** Bars still awaiting a verdict. */\n toGo: number;\n /** True once every bar is settled (and there is at least one). */\n concluded: boolean;\n}\n\nexport function experimentProgress(bars: BarLineInput[]): Progress {\n const total = bars.length;\n const settled = bars.filter(\n (b) => typeof b.barVerdict === \"string\" && b.barVerdict.trim() !== \"\",\n ).length;\n return {\n total,\n settled,\n toGo: total - settled,\n concluded: total > 0 && settled === total,\n };\n}\n","/**\n * Experiment confidence — a derived [0, 100] gauge for an evidence plan (0.14).\n *\n * Formula (`experimentConfidence`):\n * clamp(50 + 50 × C × S + 5 × A, 0, 100)\n *\n * Where:\n * B = number of bar lines\n * coveredLines = bar lines with ≥1 concluded reading (Validated/Invalidated)\n * C = |coveredLines| / max(B, 1) — coverage factor ∈ [0, 1]\n * For each concluded reading linked to the experiment (deduped by Source,\n * same rule as `confidence.ts`):\n * fᵢ = (strengthᵢ × sqᵢ) / MAX_STRENGTH (signed)\n * strengthᵢ = readingStrength({ rung, result, magnitudeBand })\n * sqᵢ = sourceQuality(representativeness, credibility)\n * F = Σ fᵢ (signed, after dedupe)\n * S = F / (1 + |F|) — soft squash, ℝ → (−1, +1)\n * A = Σ alignedⱼ / B — verdict alignment, per bar\n * alignedⱼ = +1 if barVerdict agrees with the reading sign on bar j,\n * −1 if it contradicts,\n * 0 if Inconclusive or no readings on bar j\n *\n * Neutral = 50 (no evidence yet). Validated readings fill up, Invalidated\n * pull down. Coverage-gated (no readings → C=0 → 50). The 5×A term is a small\n * verdict-alignment nudge so a plan whose bars are settled the way the evidence\n * already points inches further in that direction.\n */\nimport type { MagnitudeBand, QuestionType, Result, Rung } from \"../types.js\";\nimport { round2 } from \"./round.js\";\nimport { sourceQuality } from \"./source-quality.js\";\nimport { isConcluded, readingStrength, sign } from \"./strength.js\";\n\n/** The reference maximum strength — the anchor of the highest rung band. */\nexport const MAX_STRENGTH = 99;\n\nexport interface ExperimentConfidenceBarInput {\n assumptionId: string;\n /** Set at closure; null/absent until then. */\n barVerdict?: Result | null;\n}\n\nexport interface ExperimentConfidenceReadingInput {\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 /** The linked assumption's question type — sets the anchor sub-ladder. */\n questionType: QuestionType;\n magnitudeBand?: MagnitudeBand;\n representativeness: number;\n credibility: number;\n /** The assumption (bar line) this reading scores. */\n assumptionId: string;\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n\n/**\n * Source dedupe — same rule as `confidence.ts`: group by Source (fall back to\n * the reading's own id), keep the entry with the largest |strength|. Market\n * rungs never dedupe (each closed commitment is its own unit). Mirrored here so\n * experiment confidence stays pure and self-contained.\n */\nfunction dedupeBySource(\n readings: ExperimentConfidenceReadingInput[],\n): ExperimentConfidenceReadingInput[] {\n const MARKET = new Set<Rung>([\"Signed intent\", \"Paying users\"]);\n const best = new Map<string, ExperimentConfidenceReadingInput>();\n for (const r of readings) {\n if (MARKET.has(r.rung)) {\n best.set(r.id, r);\n continue;\n }\n const key = r.source || r.id;\n const cur = best.get(key);\n const s = Math.abs(readingStrength(r));\n const better =\n !cur ||\n s > Math.abs(readingStrength(cur)) ||\n (s === Math.abs(readingStrength(cur)) && (r.id > cur.id));\n if (better) best.set(key, r);\n }\n return [...best.values()];\n}\n\n/**\n * Compute the experiment-confidence gauge. `bars` is the experiment's bar\n * lines; `readings` is the concluded readings linked to the experiment (the\n * caller filters to the experiment's `barLineAssumptionIds`).\n */\nexport function experimentConfidence(\n bars: ExperimentConfidenceBarInput[],\n readings: ExperimentConfidenceReadingInput[],\n): number {\n const B = bars.length;\n\n // Only concluded readings enter; dedupe by Source.\n const concluded = dedupeBySource(\n readings.filter((r) => isConcluded(r.result)),\n );\n\n // Coverage: bar lines with ≥1 concluded reading.\n const barsWithReadings = new Set(\n concluded.map((r) => r.assumptionId),\n );\n const covered = bars.filter((b) => barsWithReadings.has(b.assumptionId)).length;\n const C = B > 0 ? covered / B : 0;\n\n // F = Σ fᵢ (signed).\n let F = 0;\n for (const r of concluded) {\n const strength = readingStrength(r);\n const sq = sourceQuality(r.representativeness, r.credibility);\n F += (strength * sq) / MAX_STRENGTH;\n }\n const S = F / (1 + Math.abs(F));\n\n // Verdict alignment: per bar, +1 if barVerdict agrees with the net reading\n // sign on that bar, −1 if it contradicts, 0 if Inconclusive or no readings.\n const signByBar = new Map<string, number>();\n for (const r of concluded) {\n const s = sign(r.result);\n signByBar.set(r.assumptionId, (signByBar.get(r.assumptionId) ?? 0) + s);\n }\n let A = 0;\n for (const bar of bars) {\n const net = signByBar.get(bar.assumptionId);\n if (net === undefined || net === 0) continue; // no readings or net-zero\n const v = bar.barVerdict;\n if (v !== \"Validated\" && v !== \"Invalidated\") continue; // Inconclusive / unset\n const barSign = v === \"Validated\" ? 1 : -1;\n A += net * barSign > 0 ? 1 : -1;\n }\n if (B > 0) A /= B;\n\n const raw = 50 + 50 * C * S + 5 * A;\n return round2(clamp(raw, 0, 100));\n}","/**\n * Confidence over time — the story of how the number got where it is\n * (OPS-1276). At each date a concluded reading was dated, we recompute\n * Confidence over every concluded reading up to and including that date, using\n * the very same `confidence()` the derived box uses (so the last point equals\n * the hero number). Undated concluded readings have no place on the timeline\n * but still bear on the belief, so they are treated as always-present — folded\n * into every point — which keeps the final point equal to today's Confidence.\n */\nimport { confidence, type ConfidenceReadingInput } from \"./confidence.js\";\nimport { isConcluded } from \"./strength.js\";\n\nexport interface TrajectoryPoint {\n /** ISO date at which Confidence took this value. */\n date: string;\n confidence: number;\n}\n\nexport function confidenceTrajectory(\n readings: ConfidenceReadingInput[],\n): TrajectoryPoint[] {\n const concluded = readings.filter((r) => isConcluded(r.result));\n const undated = concluded.filter((r) => !r.date);\n const dated = concluded.filter((r) => r.date);\n if (dated.length === 0) return [];\n\n const dates = [...new Set(dated.map((r) => r.date as string))].sort();\n return dates.map((date) => ({\n date,\n // Everything dated on/before this point, plus the always-present undated.\n confidence: confidence([\n ...undated,\n ...dated.filter((r) => (r.date as string) <= date),\n ]),\n }));\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 the instance's `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","/**\n * Portfolio progress — the one *cross-belief* reading (OPS-1293 / OPS-1300).\n *\n * Every other derived number here is per-record (risk/confidence/impact of one\n * belief). This is the whole-set roll-up behind the pipeline's headline: a\n * **burn-up**, \"% of identified risk bought down\" = Risk Retired ÷ Risk-ever-\n * identified across *all* beliefs, resolved ones included.\n *\n * Like `rankNextMoves`, it is a whole-set ordering computed **fresh on read**,\n * not stored — it stays out of the OPS-1251 on-write recompute (it only reads\n * numbers already kept current). Pure and numeric: the dashboard maps records\n * to these inputs (as `understanding.ts` maps readings), so the rule lives\n * once, here.\n *\n * The model (matches the OPS-1296 prototype's self-consistent maths):\n * - **ever-identified** for a belief = the risk it represented at Confidence\n * ≤ 0, i.e. its Derived Impact. A moot row's Derived Impact is pinned to 0,\n * which would erase it from *both* sides of the fraction, so we floor\n * ever-identified at the hand-scored seed Impact — mooting a belief must\n * *retire* its risk, never shrink the denominator (the burn-up's whole\n * point: resolved risk stays counted).\n * - **live** risk = the belief's current Risk, or 0 once resolved (a kill or\n * a moot resolves the uncertainty — that risk is bought down, not carried).\n * - **retired** = ever-identified − live.\n */\nimport { round2 } from \"./round.js\";\n\nexport interface PortfolioBeliefInput {\n id: string;\n /** Derived Impact — the belief's risk at Confidence ≤ 0 (0 when moot). */\n derivedImpact: number;\n /** The hand-scored seed Impact; the ever-identified floor when moot zeroes\n * Derived Impact. Null/absent treated as 0. */\n seedImpact: number | null;\n /** The belief's current live Risk (stored `derived.risk`). */\n risk: number;\n /** Resolved — Invalidated (killed) or moot. Its live risk reads 0 (fully\n * retired), whatever the stored Risk number says. */\n resolved: boolean;\n}\n\n/** One belief's contribution to the burn-up. */\nexport interface BeliefRisk {\n /** Risk-ever-identified — the denominator's per-belief share. */\n identified: number;\n /** Live risk still carried (0 once resolved). */\n live: number;\n /** Risk bought down — the numerator's per-belief share. */\n retired: number;\n}\n\nexport interface PortfolioProgress {\n /** Σ risk-ever-identified — the burn-up denominator. */\n identified: number;\n /** Σ risk bought down — the burn-up numerator. */\n retired: number;\n /** Σ risk still live. */\n live: number;\n /** Retired ÷ identified as a percentage (0 when nothing is identified). */\n percent: number;\n /** Beliefs still in play (not resolved). */\n liveCount: number;\n /** Beliefs resolved (killed or moot). */\n resolvedCount: number;\n}\n\n/** One belief's identified / live / retired risk — the rule in one place. */\nexport function beliefRisk(b: PortfolioBeliefInput): BeliefRisk {\n const seed = b.seedImpact ?? 0;\n const identified = Math.max(b.derivedImpact, seed, 0);\n // Live risk can never exceed what was ever identified — the clamp keeps a\n // stale/over-large stored Risk from making retired go negative.\n const live = b.resolved ? 0 : Math.min(identified, Math.max(0, b.risk));\n return {\n identified: round2(identified),\n live: round2(live),\n retired: round2(identified - live),\n };\n}\n\n/**\n * Roll every belief up into the portfolio burn-up. Pass the *whole* set\n * (resolved rows included) — a filtered slice understates the denominator and\n * makes fresh or retired risk read as backsliding.\n */\nexport function portfolioProgress(\n beliefs: PortfolioBeliefInput[],\n): PortfolioProgress {\n let identified = 0;\n let live = 0;\n let liveCount = 0;\n let resolvedCount = 0;\n for (const b of beliefs) {\n const r = beliefRisk(b);\n identified += r.identified;\n live += r.live;\n if (b.resolved) resolvedCount += 1;\n else liveCount += 1;\n }\n const retired = identified - live;\n return {\n identified: round2(identified),\n retired: round2(retired),\n live: round2(live),\n percent: identified > 0 ? round2((retired / identified) * 100) : 0,\n liveCount,\n resolvedCount,\n };\n}\n","/**\n * Next-move ranking — the front door's single source of truth for \"what should\n * I do next\" (build OPS-1304; placement OPS-1292; action vocabulary OPS-1291).\n *\n * Ranks *beliefs* — Model A: point at one belief, not a heterogeneous triage\n * queue (OPS-1291) — by the method's Feasibility × Risk rule (`docs/method.md`,\n * `ontology.yaml` → `derived_views.next_move`), and names the single act each\n * belief's stage demands. A belief at Confidence ≤ −50 jumps into a distinct\n * kill/re-test lane that sorts above the Feasibility × Risk order regardless of\n * rank — the one place act-urgency beats belief-risk (`derived_views.kill_lane`).\n *\n * Computed fresh on read: a whole-set ordering, so it stays OUT of the OPS-1251\n * on-write recompute — it reads the derived numbers (Risk, Confidence) those\n * writes already keep current. Pure: no I/O, no caching, no weights framework —\n * the enum→multiplier map below IS the formula (OPS-1292: \"no weights /\n * strategies / caching / framework\").\n */\nimport type { Feasibility } from \"../types.js\";\nimport { round2 } from \"./round.js\";\n\n/** The kill/re-test threshold — Confidence at or below this is the kill lane. */\nexport const KILL_LANE_THRESHOLD = -50;\n\n/**\n * The acts the front door can name, one per belief-stage. The front door\n * *names* all of them; only a subset are human step-in forms — the rest are\n * agent-run / off-dashboard (OPS-1294). Which is which is a presentation\n * concern the dashboard owns, not this ranking.\n */\nexport type MoveKind =\n | \"score-impact\" // Framed — the belief isn't weighted yet (Impact unscored)\n | \"design-experiment\" // Planned — no test plans this belief yet\n | \"record-reading\" // Tested — a test is running, evidence still landing\n | \"decide\" // Known — evidence has landed and no decision rests on it\n | \"retest\"; // kill lane — Confidence ≤ −50, jumps the ordering\n\n/**\n * One belief's next move. `move`/`score`/`reason` are the OPS-1292 output\n * contract; the rest is context the front door renders (the risk chip, the kill\n * banner, the step-in adaptation) — every field read from the inputs, nothing\n * new computed here.\n */\nexport interface NextMove {\n /** The act this belief's stage demands. */\n move: MoveKind;\n assumptionId: string;\n /** The belief statement — the hero headline. */\n title: string;\n /** Feasibility × Risk. Kill-lane rows carry their Risk and sort first. */\n score: number;\n /** Plain-language \"why this\" — explains from the inputs, no jargon. */\n reason: string;\n /** The belief's live derived Risk (0–100). */\n risk: number;\n /** The belief's live derived Confidence (signed −100…100). */\n confidence: number;\n /** The feasibility that fed the score; null when no test plans it yet. */\n feasibility: Feasibility | null;\n /** Confidence ≤ −50 — the override lane. */\n killLane: boolean;\n}\n\n/** A belief and its live derived numbers (the ranking's primary input). */\nexport interface NextMoveAssumptionInput {\n id: string;\n title: string;\n /** AssumptionStatus; `Invalidated` rows are already killed and drop out. */\n status: string;\n /** The hand-scored seed (0–100); null means unscored → \"score impact\". */\n impact: number | null;\n /** Mooted beliefs (Impact pinned to 0 by a decision) drop out of ranking. */\n moot: boolean;\n /** Derived Risk (already recomputed on write — read, never recomputed). */\n risk: number;\n /** Derived Confidence (already recomputed on write). */\n confidence: number;\n /** How many concluded (Validated/Invalidated) readings back this belief. */\n concludedReadings: number;\n}\n\n/** An experiment, reduced to what stage + feasibility resolution needs. */\nexport interface NextMoveExperimentInput {\n /** ExperimentStatus — \"Running\" | \"Closed\". */\n status: string;\n feasibility: Feasibility | null;\n /** The assumption ids this experiment's bar lines name. */\n assumptionIds: string[];\n}\n\n/** A decision, reduced to which beliefs it rests on or resolves. */\nexport interface NextMoveDecisionInput {\n /** DecisionStatus; only standing (Active/Provisional) decisions count. */\n status: string;\n /** The assumption ids this decision rests on (`based on`) or resolves. */\n assumptionIds: string[];\n}\n\nexport interface NextMoveInput {\n assumptions: NextMoveAssumptionInput[];\n experiments: NextMoveExperimentInput[];\n decisions: NextMoveDecisionInput[];\n}\n\n/**\n * Feasibility → the score multiplier. Cheaper (more feasible) tests rank\n * higher, so the cheapest honest test of the riskiest belief is on top\n * (`method.md`). A belief with no planned test yet has unknown feasibility →\n * the neutral middle, so Risk decides its place honestly.\n */\nconst FEASIBILITY_WEIGHT: Record<Feasibility, number> = {\n High: 1,\n Medium: 0.6,\n Low: 0.3,\n};\nconst NEUTRAL_FEASIBILITY_WEIGHT = FEASIBILITY_WEIGHT.Medium;\n\nfunction isStanding(decisionStatus: string): boolean {\n return decisionStatus === \"Active\" || decisionStatus === \"Provisional\";\n}\n\n/** Pick the cheapest (highest-weight) feasibility among a belief's tests. */\nfunction bestFeasibility(\n feasibilities: (Feasibility | null)[],\n): Feasibility | null {\n let best: Feasibility | null = null;\n let bestWeight = -1;\n for (const f of feasibilities) {\n if (!f) continue;\n const w = FEASIBILITY_WEIGHT[f];\n if (w > bestWeight) {\n bestWeight = w;\n best = f;\n }\n }\n return best;\n}\n\n/**\n * The act a belief's stage demands (before the kill-lane override).\n *\n * Deliberately NOT the `stage.ts` structural classifier: that answers \"how far\n * along the Framed→Planned→Tested→Known artifacts are\" (framing completeness,\n * bar-line settlement); this answers \"what should the founder do next\" from the\n * evidence state (is it weighted, has evidence concluded, does a test run, does\n * a decision rest on it). Same belief can be structurally `tested` yet owe a\n * `decide` here — the journey rail (stage) and next-move card (act) are two\n * readings of one belief, so they share the test-plan *meter* (`beliefTestMeters`)\n * but not the classifier.\n */\nfunction stageMove(\n a: NextMoveAssumptionInput,\n hasRunningTest: boolean,\n hasAnyTest: boolean,\n hasStandingDecision: boolean,\n): MoveKind | null {\n if (a.impact === null) return \"score-impact\"; // Framed → weight it\n if (a.concludedReadings === 0) {\n // No evidence yet: if a test is running, wait for readings; else plan one.\n return hasRunningTest ? \"record-reading\" : \"design-experiment\";\n }\n // Evidence has landed. If nothing rests on it yet, it's time to decide;\n // once a standing decision does, the belief is resolved and drops out.\n if (!hasStandingDecision) return \"decide\";\n return null;\n}\n\n/** Plain-language \"why this\" — explains the move from the inputs. */\nfunction reasonFor(move: MoveKind, a: NextMoveAssumptionInput): string {\n switch (move) {\n case \"retest\":\n return `Confidence has fallen to ${Math.round(a.confidence)} — the evidence is turning against this belief. Kill it or test it again.`;\n case \"score-impact\":\n return \"This belief isn't weighted yet — score its impact so its risk can rank against the rest.\";\n case \"design-experiment\":\n return `Your riskiest untested belief. Design the cheapest honest test that could move it.`;\n case \"record-reading\":\n return \"A test is running against this belief — evidence is still landing.\";\n case \"decide\":\n return \"The evidence is in and nothing rests on it yet — time to make the call.\";\n }\n}\n\n/**\n * Rank every unresolved belief into its next move (Model A). Returns a plain\n * sorted list, most-pressing first: the kill lane on top (by Risk), then the\n * rest by Feasibility × Risk, tie-broken by the most-negative Confidence\n * (`derived_views.test_next_surface`), then by id for a stable order. The front\n * door takes the head as the hero and the tail as \"On deck\" / manual override.\n */\nexport function rankNextMoves(input: NextMoveInput): NextMove[] {\n const { assumptions, experiments, decisions } = input;\n\n // Which experiments (and their feasibilities/statuses) name each belief.\n const testsByAssumption = new Map<\n string,\n { running: boolean; any: boolean; feasibilities: (Feasibility | null)[] }\n >();\n for (const exp of experiments) {\n const running = exp.status === \"Running\";\n for (const id of exp.assumptionIds) {\n const entry = testsByAssumption.get(id) ?? {\n running: false,\n any: false,\n feasibilities: [],\n };\n entry.any = true;\n entry.running = entry.running || running;\n entry.feasibilities.push(exp.feasibility);\n testsByAssumption.set(id, entry);\n }\n }\n\n // Which beliefs a standing decision rests on or resolves.\n const decidedAssumptions = new Set<string>();\n for (const d of decisions) {\n if (!isStanding(d.status)) continue;\n for (const id of d.assumptionIds) decidedAssumptions.add(id);\n }\n\n const moves: NextMove[] = [];\n for (const a of assumptions) {\n if (a.moot || a.status === \"Invalidated\") continue;\n\n const killLane = a.confidence <= KILL_LANE_THRESHOLD;\n const tests = testsByAssumption.get(a.id);\n const feasibility = bestFeasibility(tests?.feasibilities ?? []);\n\n const move: MoveKind | null = killLane\n ? \"retest\"\n : stageMove(\n a,\n tests?.running ?? false,\n tests?.any ?? false,\n decidedAssumptions.has(a.id),\n );\n if (move === null) continue; // resolved — no move\n\n const weight = feasibility\n ? FEASIBILITY_WEIGHT[feasibility]\n : NEUTRAL_FEASIBILITY_WEIGHT;\n // Kill-lane rows carry their raw Risk so they order among themselves by\n // Risk; the killLane flag (not the score) floats them above everyone else.\n const score = round2(killLane ? a.risk : weight * a.risk);\n\n moves.push({\n move,\n assumptionId: a.id,\n title: a.title,\n score,\n reason: reasonFor(move, a),\n risk: a.risk,\n confidence: a.confidence,\n feasibility,\n killLane,\n });\n }\n\n moves.sort((x, y) => {\n if (x.killLane !== y.killLane) return x.killLane ? -1 : 1;\n if (y.score !== x.score) return y.score - x.score;\n if (x.confidence !== y.confidence) return x.confidence - y.confidence;\n return x.assumptionId < y.assumptionId ? -1 : 1;\n });\n return moves;\n}\n","/**\n * Per-belief stage — where one belief sits on the loop's four-stage spine\n * (Framed → Planned → Tested → Known) and its four meters (OPS-1329).\n *\n * This is the *single-belief* analogue of the pipeline's cross-belief roll-up:\n * the pipeline row-builder used to derive framing / test-plan / test-progress\n * inline, and the front-door move ladder (OPS-1304) re-walked the same stages a\n * second way. The shared classification lives here now, so the pipeline board,\n * the front door, and the per-belief journey rail all read one rule rather than\n * three that can drift.\n *\n * Pure and computed fresh on read (like `portfolio.ts` / `next-move.ts`): it\n * only reads numbers already kept current, so it stays out of the OPS-1251\n * on-write recompute. The record → meter mapping (bar lines, presence fields)\n * lives in the dashboard, as elsewhere; this module takes the reduced inputs.\n */\nimport { KILL_LANE_THRESHOLD } from \"./next-move.js\";\n\n/** The four loop stages a belief travels, in order (OPS-1293). */\nexport type StageKey = \"framed\" | \"planned\" | \"tested\" | \"known\";\n\n/** Sign of a belief's Confidence — the Known meter's direction. */\nexport type ConfSign = \"pos\" | \"neg\" | \"zero\";\n\n/** One belief's test state, aggregated across every experiment aimed at it. */\nexport interface TestMeter {\n /** A bar line (or the convenience projection) names this belief. */\n planned: boolean;\n /** Pre-registered bars that have a verdict. */\n settled: number;\n /** Pre-registered bars in total. */\n total: number;\n}\n\n/** A test's bar lines, reduced to what the meter needs (register-agnostic). */\nexport interface StageExperimentInput {\n /** Each bar line naming a belief, and whether it has settled (has a verdict). */\n bars: { assumptionId: string; settled: boolean }[];\n /** Beliefs this test plans via the convenience projection (bars may be unexpanded). */\n plannedAssumptionIds: string[];\n}\n\n/** The reduced inputs one belief's stage is derived from. */\nexport interface BeliefStageInput {\n /** Framing completeness, 0–100 (`assumptionCompleteness`). */\n framed: number;\n /** Live derived Confidence (signed −100…100). */\n confidence: number;\n /** This belief's aggregated test state. */\n test: TestMeter;\n}\n\n/** One belief's position on the spine plus its four meters. */\nexport interface BeliefStage {\n /** Where the belief sits: framed → planned → tested → known. */\n stage: StageKey;\n /** Meter 1 — framing completeness, 0–100. */\n framed: number;\n /** Meter 2 — a test has been designed against this belief. */\n planned: boolean;\n /** Meter 3 — pre-registered bars settled / total. */\n tested: { settled: number; total: number };\n /** Meter 4 — signed Known: the belief's Confidence. */\n confidence: number;\n /** Sign bucket for the Known gauge direction. */\n confSign: ConfSign;\n /** Confidence ≤ −50 — the kill/re-test overlay (the same lane as the front door). */\n killZone: boolean;\n}\n\n/** An empty test state — a belief no experiment has named yet. */\nexport function emptyTestMeter(): TestMeter {\n return { planned: false, settled: 0, total: 0 };\n}\n\n/**\n * For every belief, the state of the tests aimed at it — whether one is designed\n * and how many of its pre-registered bars have settled, aggregated across all\n * experiments. Factored out of the pipeline row-builder so the board and a\n * single belief's rail agree by construction.\n */\nexport function beliefTestMeters(\n experiments: StageExperimentInput[],\n): Map<string, TestMeter> {\n const byAssumption = new Map<string, TestMeter>();\n const ensure = (id: string): TestMeter => {\n let s = byAssumption.get(id);\n if (!s) {\n s = emptyTestMeter();\n byAssumption.set(id, s);\n }\n return s;\n };\n for (const exp of experiments) {\n for (const b of exp.bars) {\n if (!b.assumptionId) continue;\n const s = ensure(b.assumptionId);\n s.planned = true;\n s.total += 1;\n if (b.settled) s.settled += 1;\n }\n // A designed test may name a belief via the convenience projection even when\n // its bar lines aren't expanded — still counts as Planned.\n for (const id of exp.plannedAssumptionIds) ensure(id).planned = true;\n }\n return byAssumption;\n}\n\n/**\n * Classify a belief on the spine from its meters. The kill-zone overlay is\n * *not* a stage — a belief whose evidence has turned is still structurally\n * wherever its framing/tests put it (a re-test moves it backward via the Known\n * meter, OPS-1300), so this stays pure status.\n */\nexport function classifyStage(framed: number, test: TestMeter): StageKey {\n if (framed < 100) return \"framed\";\n if (!test.planned) return \"planned\";\n if (test.total === 0 || test.settled < test.total) return \"tested\";\n return \"known\";\n}\n\n/** One belief's stage + four meters — the rail's data. */\nexport function deriveBeliefStage(input: BeliefStageInput): BeliefStage {\n const { framed, confidence, test } = input;\n return {\n stage: classifyStage(framed, test),\n framed,\n planned: test.planned,\n tested: { settled: test.settled, total: test.total },\n confidence,\n confSign: confidence > 0 ? \"pos\" : confidence < 0 ? \"neg\" : \"zero\",\n killZone: confidence <= KILL_LANE_THRESHOLD,\n };\n}\n","/**\n * The per-belief journey event log (OPS-1329) — the belief's life ordered into\n * dated events: bet → score → experiment → readings → confidence-cross → now.\n * It is the *story* half of the journey drill-in (the *rail* half is `stage.ts`).\n *\n * No new maths: Confidence at each point is read off `confidenceTrajectory`\n * (the very numbers the understanding layer already shows), and the current\n * number off `confidence()`. Nothing is invented — an event whose underlying\n * datum is absent (no impact score, no experiment, no concluded reading, no\n * kill-zone cross) is simply omitted, and no date is ever faked (an event with\n * no real date carries `date: null` and takes its place by structural order).\n *\n * Pure and label-free: the dashboard journey view-model adds the copy. Computed\n * fresh on read, out of the OPS-1251 on-write recompute.\n */\nimport type { Result } from \"../types.js\";\nimport type { AttributionReadingInput } from \"./attribution.js\";\nimport { confidence } from \"./confidence.js\";\nimport { KILL_LANE_THRESHOLD } from \"./next-move.js\";\nimport { isConcluded } from \"./strength.js\";\nimport { confidenceTrajectory } from \"./trajectory.js\";\n\n/** The kinds of event a belief's life produces, in structural order. */\nexport type JourneyEventKind =\n | \"bet\" // the belief was written\n | \"score\" // its impact was scored\n | \"experiment\" // a test was designed against it\n | \"reading\" // evidence landed\n | \"confidence-cross\" // Confidence fell into the kill zone\n | \"now\"; // the belief's state today\n\n/** The structural order the story walks — the tie-break for undated events. */\nconst KIND_ORDER: Record<JourneyEventKind, number> = {\n bet: 0,\n score: 1,\n experiment: 2,\n reading: 3,\n \"confidence-cross\": 4,\n now: 5,\n};\n\n/** One event in a belief's life. Label-free — the view-model adds the copy. */\nexport interface JourneyEvent {\n kind: JourneyEventKind;\n /** ISO date, or null when no real date exists (never faked). */\n date: string | null;\n /** Confidence known at this event (reading / cross / now); null otherwise. */\n confidence: number | null;\n /** The reading's verdict, for `reading` events; null otherwise. */\n result: Result | null;\n /** The source record id (reading id, experiment id) when the event has one. */\n refId: string | null;\n}\n\n/** The belief itself, reduced to what the log needs. */\nexport interface JourneyBeliefInput {\n /** When the bet was written — the `bet` event's date. */\n createdAt: string | null;\n /** Impact has been scored (a non-null seed) — emits the `score` event. */\n impactScored: boolean;\n}\n\n/** A test aimed at this belief, reduced to what the log needs. */\nexport interface JourneyExperimentInput {\n id: string;\n /** When the test was designed; null when unknown (event still emitted, undated). */\n date: string | null;\n}\n\nexport interface AssembleJourneyInput {\n belief: JourneyBeliefInput;\n /** The belief's own readings (already filtered to this assumption). */\n readings: AttributionReadingInput[];\n /** The experiments testing this belief. */\n experiments: JourneyExperimentInput[];\n /** \"Now\" as an ISO date — passed in so the log stays pure. */\n now: string;\n}\n\n/**\n * Assemble one belief's chronological event log. Events sort by date, undated\n * events anchored to the bet's date and ordered structurally; `now` is always\n * last.\n */\nexport function assembleJourney(input: AssembleJourneyInput): JourneyEvent[] {\n const { belief, readings, experiments, now } = input;\n const events: JourneyEvent[] = [];\n\n if (belief.createdAt) {\n events.push(event(\"bet\", belief.createdAt));\n }\n if (belief.impactScored) {\n // No scored-on date is stored, so the score rides structurally after the\n // bet rather than carrying an invented date.\n events.push(event(\"score\", null));\n }\n for (const e of experiments) {\n events.push(event(\"experiment\", e.date, { refId: e.id }));\n }\n\n const trajectory = confidenceTrajectory(readings);\n const confByDate = new Map(trajectory.map((p) => [p.date, p.confidence]));\n for (const r of readings) {\n const dated = r.date ?? null;\n // Confidence is known only for a concluded reading that lands on a\n // trajectory date (undated / inconclusive readings carry no number).\n const conf =\n dated && isConcluded(r.result) ? (confByDate.get(dated) ?? null) : null;\n events.push(\n event(\"reading\", dated, { confidence: conf, result: r.result, refId: r.id }),\n );\n }\n\n // The first point at which the evidence dragged Confidence into the kill zone.\n const cross = trajectory.find((p) => p.confidence <= KILL_LANE_THRESHOLD);\n if (cross) {\n events.push(event(\"confidence-cross\", cross.date, { confidence: cross.confidence }));\n }\n\n // Undated events anchor to the bet so they sit at the start in structural\n // order; `now` is appended after sorting so it is unconditionally last.\n const anchor = belief.createdAt ?? \"\";\n events.sort((a, b) => {\n const ka = a.date ?? anchor;\n const kb = b.date ?? anchor;\n if (ka !== kb) return ka < kb ? -1 : 1;\n return KIND_ORDER[a.kind] - KIND_ORDER[b.kind];\n });\n events.push(event(\"now\", now, { confidence: confidence(readings) }));\n\n return events;\n}\n\nfunction event(\n kind: JourneyEventKind,\n date: string | null,\n extra: Partial<Pick<JourneyEvent, \"confidence\" | \"result\" | \"refId\">> = {},\n): JourneyEvent {\n return {\n kind,\n date,\n confidence: extra.confidence ?? null,\n result: extra.result ?? null,\n refId: extra.refId ?? null,\n };\n}\n"],"mappings":"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","names":["risk","confidence","band","band","confidence","confidence"]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { e as AssembleJourneyInput, f as Attribution, A as AttributionReadingInput, B as BarLineInput, g as BeliefRisk, h as BeliefStage, j as BeliefStageInput, k as COMMITMENT_FOUND, C as COMPLETENESS_SLOTS, b as CompletenessInput, a as CompletenessSlot,
|
|
1
|
+
export { e as AssembleJourneyInput, f as Attribution, A as AttributionReadingInput, B as BarLineInput, g as BeliefRisk, h as BeliefStage, j as BeliefStageInput, k as COMMITMENT_FOUND, C as COMPLETENESS_SLOTS, l as CONFIDENCE_FLOOR_BY_STAGE, b as CompletenessInput, a as CompletenessSlot, n as ConfSign, o as ConfidenceReadingInput, D as DEFAULT_QUESTION_TYPE, E as ExperimentConfidenceBarInput, p as ExperimentConfidenceReadingInput, I as INFERABLE_QUESTION_TYPES, q as ImpactAssumptionInput, J as JourneyBeliefInput, t as JourneyEvent, u as JourneyEventKind, v as JourneyExperimentInput, K as KILL_LANE_THRESHOLD, M as MAX_STRENGTH, w as MigratedAssumption, x as MigratedReading, y as MigrationAssumption, z as MigrationReading, F as MigrationResult, G as MoveKind, H as Mover, L as MoverKind, N as NextMove, O as NextMoveAssumptionInput, P as NextMoveDecisionInput, Q as NextMoveExperimentInput, R as NextMoveInput, S as PortfolioBeliefInput, T as PortfolioProgress, U as Progress, V as RISK_THRESHOLD_BY_STAGE, W as RUNG_ANCHOR, X as Scored, Y as StageExperimentInput, Z as StageKey, _ as StrengthInput, $ as TestMeter, a0 as TrajectoryPoint, a1 as W0, a2 as W0_BY_RUNG, a3 as assembleJourney, d as assumptionComplete, c as assumptionCompleteness, a4 as beliefRisk, a5 as beliefTestMeters, a6 as ceilingAnchor, a7 as classifyStage, a8 as commitmentFactor, a9 as completenessSlotPresence, aa as confidence, ab as confidenceAttribution, ac as confidenceFloorForStage, ad as confidenceTrajectory, ae as deriveBeliefStage, af as derivedImpacts, ag as emptyTestMeter, ah as experimentConfidence, ai as experimentProgress, aj as hasClearedThreshold, ak as inferQuestionType, al as isConcluded, am as isNonEvidence, an as migrateRegister, m as missingCompletenessSlots, ao as needsReview, ap as portfolioProgress, aq as rankNextMoves, r as readingStrength, ar as risk, as as riskThresholdForStage, at as round2, au as scoreAndDedupe, av as sign, s as sourceQuality, aw as w0ForRung } from '../index-DhQyc0Gj.js';
|
|
2
2
|
import '../types-DZIQEO4D.js';
|
package/dist/derivation/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
COMMITMENT_FOUND,
|
|
3
3
|
COMPLETENESS_SLOTS,
|
|
4
|
+
CONFIDENCE_FLOOR_BY_STAGE,
|
|
4
5
|
DEFAULT_QUESTION_TYPE,
|
|
5
6
|
INFERABLE_QUESTION_TYPES,
|
|
6
7
|
KILL_LANE_THRESHOLD,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
completenessSlotPresence,
|
|
21
22
|
confidence,
|
|
22
23
|
confidenceAttribution,
|
|
24
|
+
confidenceFloorForStage,
|
|
23
25
|
confidenceTrajectory,
|
|
24
26
|
deriveBeliefStage,
|
|
25
27
|
derivedImpacts,
|
|
@@ -43,11 +45,12 @@ import {
|
|
|
43
45
|
sign,
|
|
44
46
|
sourceQuality,
|
|
45
47
|
w0ForRung
|
|
46
|
-
} from "../chunk-
|
|
48
|
+
} from "../chunk-TCKAMOAK.js";
|
|
47
49
|
import "../chunk-PZ5AY32C.js";
|
|
48
50
|
export {
|
|
49
51
|
COMMITMENT_FOUND,
|
|
50
52
|
COMPLETENESS_SLOTS,
|
|
53
|
+
CONFIDENCE_FLOOR_BY_STAGE,
|
|
51
54
|
DEFAULT_QUESTION_TYPE,
|
|
52
55
|
INFERABLE_QUESTION_TYPES,
|
|
53
56
|
KILL_LANE_THRESHOLD,
|
|
@@ -67,6 +70,7 @@ export {
|
|
|
67
70
|
completenessSlotPresence,
|
|
68
71
|
confidence,
|
|
69
72
|
confidenceAttribution,
|
|
73
|
+
confidenceFloorForStage,
|
|
70
74
|
confidenceTrajectory,
|
|
71
75
|
deriveBeliefStage,
|
|
72
76
|
derivedImpacts,
|
|
@@ -88,8 +88,25 @@ declare function ceilingAnchor(questionType: QuestionType, rung: Rung): number;
|
|
|
88
88
|
* consumed by the dashboard's test-next surface, the `/assumptions audit`
|
|
89
89
|
* skill, and the `/experiment-design` skill. It does NOT enter the
|
|
90
90
|
* Confidence formula.
|
|
91
|
+
*
|
|
92
|
+
* The threshold is the max Risk you can have and still "stop testing" for
|
|
93
|
+
* this stage. Later stages have LOWER thresholds because you need to drive
|
|
94
|
+
* Risk down further (more evidence) before acting on a one-way door.
|
|
95
|
+
* Discovery = 30: stop testing when Risk ≤ 30 (two-way door, little evidence
|
|
96
|
+
* needed). Maturity = 5: stop testing when Risk ≤ 5 (one-way door, lots of
|
|
97
|
+
* evidence needed). The bar being LOWER means the STANDARD is HIGHER — like
|
|
98
|
+
* a high-jump bar: a lower number means you have to clear more.
|
|
91
99
|
*/
|
|
92
100
|
declare const RISK_THRESHOLD_BY_STAGE: Record<Stage, number>;
|
|
101
|
+
/**
|
|
102
|
+
* The minimum Confidence floor for "cleared" (DEV-5890 fix). A belief with
|
|
103
|
+
* Impact below the stage's Risk threshold could read Risk ≤ threshold with
|
|
104
|
+
* ZERO evidence (Risk = Impact × (1 − 0/100) = Impact). Without a Confidence
|
|
105
|
+
* floor, a low-Impact belief gets a free pass. The floor requires at least
|
|
106
|
+
* some evidence before "cleared" is honest. Tightens with stage, mirroring
|
|
107
|
+
* the Risk threshold.
|
|
108
|
+
*/
|
|
109
|
+
declare const CONFIDENCE_FLOOR_BY_STAGE: Record<Stage, number>;
|
|
93
110
|
/**
|
|
94
111
|
* The Risk threshold for a given stage — the stopping rule for attention.
|
|
95
112
|
* Falls back to the tightest threshold (Maturity) when the stage is absent,
|
|
@@ -97,11 +114,22 @@ declare const RISK_THRESHOLD_BY_STAGE: Record<Stage, number>;
|
|
|
97
114
|
*/
|
|
98
115
|
declare function riskThresholdForStage(stage: Stage | null | undefined): number;
|
|
99
116
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
|
|
117
|
+
* The minimum Confidence floor for a given stage. Falls back to the tightest
|
|
118
|
+
* (Maturity) when absent.
|
|
119
|
+
*/
|
|
120
|
+
declare function confidenceFloorForStage(stage: Stage | null | undefined): number;
|
|
121
|
+
/**
|
|
122
|
+
* Has the assumption cleared its stage's threshold? "Cleared" requires BOTH:
|
|
123
|
+
* 1. Risk ≤ the stage's Risk threshold (enough evidence to drive Risk down)
|
|
124
|
+
* 2. Confidence ≥ the stage's Confidence floor (at least some real evidence)
|
|
125
|
+
*
|
|
126
|
+
* The Confidence floor prevents a low-Impact belief from being "cleared" with
|
|
127
|
+
* zero evidence — Risk = Impact × (1 − 0/100) = Impact, so a belief with
|
|
128
|
+
* Impact below the threshold would read "cleared" without any readings. The
|
|
129
|
+
* floor requires at least a minimum Confidence signal before "cleared" is
|
|
130
|
+
* honest. "Needs evidence" = either condition fails — testing-priority.
|
|
103
131
|
*/
|
|
104
|
-
declare function hasClearedThreshold(risk: number, stage: Stage | null | undefined): boolean;
|
|
132
|
+
declare function hasClearedThreshold(risk: number, stage: Stage | null | undefined, confidence?: number): boolean;
|
|
105
133
|
|
|
106
134
|
/**
|
|
107
135
|
* Assumption completeness — a derived readiness meter, never stored.
|
|
@@ -895,6 +923,7 @@ type index_BeliefStage = BeliefStage;
|
|
|
895
923
|
type index_BeliefStageInput = BeliefStageInput;
|
|
896
924
|
declare const index_COMMITMENT_FOUND: typeof COMMITMENT_FOUND;
|
|
897
925
|
declare const index_COMPLETENESS_SLOTS: typeof COMPLETENESS_SLOTS;
|
|
926
|
+
declare const index_CONFIDENCE_FLOOR_BY_STAGE: typeof CONFIDENCE_FLOOR_BY_STAGE;
|
|
898
927
|
type index_CompletenessInput = CompletenessInput;
|
|
899
928
|
type index_CompletenessSlot = CompletenessSlot;
|
|
900
929
|
type index_ConfSign = ConfSign;
|
|
@@ -947,6 +976,7 @@ declare const index_commitmentFactor: typeof commitmentFactor;
|
|
|
947
976
|
declare const index_completenessSlotPresence: typeof completenessSlotPresence;
|
|
948
977
|
declare const index_confidence: typeof confidence;
|
|
949
978
|
declare const index_confidenceAttribution: typeof confidenceAttribution;
|
|
979
|
+
declare const index_confidenceFloorForStage: typeof confidenceFloorForStage;
|
|
950
980
|
declare const index_confidenceTrajectory: typeof confidenceTrajectory;
|
|
951
981
|
declare const index_deriveBeliefStage: typeof deriveBeliefStage;
|
|
952
982
|
declare const index_derivedImpacts: typeof derivedImpacts;
|
|
@@ -971,7 +1001,7 @@ declare const index_sign: typeof sign;
|
|
|
971
1001
|
declare const index_sourceQuality: typeof sourceQuality;
|
|
972
1002
|
declare const index_w0ForRung: typeof w0ForRung;
|
|
973
1003
|
declare namespace index {
|
|
974
|
-
export { type index_AssembleJourneyInput as AssembleJourneyInput, type index_Attribution as Attribution, type index_AttributionReadingInput as AttributionReadingInput, type index_BarLineInput as BarLineInput, type index_BeliefRisk as BeliefRisk, type index_BeliefStage as BeliefStage, type index_BeliefStageInput as BeliefStageInput, index_COMMITMENT_FOUND as COMMITMENT_FOUND, index_COMPLETENESS_SLOTS as COMPLETENESS_SLOTS, type index_CompletenessInput as CompletenessInput, type index_CompletenessSlot as CompletenessSlot, type index_ConfSign as ConfSign, type index_ConfidenceReadingInput as ConfidenceReadingInput, index_DEFAULT_QUESTION_TYPE as DEFAULT_QUESTION_TYPE, type index_ExperimentConfidenceBarInput as ExperimentConfidenceBarInput, type index_ExperimentConfidenceReadingInput as ExperimentConfidenceReadingInput, index_INFERABLE_QUESTION_TYPES as INFERABLE_QUESTION_TYPES, type index_ImpactAssumptionInput as ImpactAssumptionInput, type index_JourneyBeliefInput as JourneyBeliefInput, type index_JourneyEvent as JourneyEvent, type index_JourneyEventKind as JourneyEventKind, type index_JourneyExperimentInput as JourneyExperimentInput, index_KILL_LANE_THRESHOLD as KILL_LANE_THRESHOLD, index_MAX_STRENGTH as MAX_STRENGTH, type index_MigratedAssumption as MigratedAssumption, type index_MigratedReading as MigratedReading, type index_MigrationAssumption as MigrationAssumption, type index_MigrationReading as MigrationReading, type index_MigrationResult as MigrationResult, type index_MoveKind as MoveKind, type index_Mover as Mover, type index_MoverKind as MoverKind, type index_NextMove as NextMove, type index_NextMoveAssumptionInput as NextMoveAssumptionInput, type index_NextMoveDecisionInput as NextMoveDecisionInput, type index_NextMoveExperimentInput as NextMoveExperimentInput, type index_NextMoveInput as NextMoveInput, type index_PortfolioBeliefInput as PortfolioBeliefInput, type index_PortfolioProgress as PortfolioProgress, type index_Progress as Progress, index_RISK_THRESHOLD_BY_STAGE as RISK_THRESHOLD_BY_STAGE, index_RUNG_ANCHOR as RUNG_ANCHOR, type index_Scored as Scored, type index_StageExperimentInput as StageExperimentInput, type index_StageKey as StageKey, type index_StrengthInput as StrengthInput, type index_TestMeter as TestMeter, type index_TrajectoryPoint as TrajectoryPoint, index_W0 as W0, index_W0_BY_RUNG as W0_BY_RUNG, index_assembleJourney as assembleJourney, index_assumptionComplete as assumptionComplete, index_assumptionCompleteness as assumptionCompleteness, index_beliefRisk as beliefRisk, index_beliefTestMeters as beliefTestMeters, index_ceilingAnchor as ceilingAnchor, index_classifyStage as classifyStage, index_commitmentFactor as commitmentFactor, index_completenessSlotPresence as completenessSlotPresence, index_confidence as confidence, index_confidenceAttribution as confidenceAttribution, index_confidenceTrajectory as confidenceTrajectory, index_deriveBeliefStage as deriveBeliefStage, index_derivedImpacts as derivedImpacts, index_emptyTestMeter as emptyTestMeter, index_experimentConfidence as experimentConfidence, index_experimentProgress as experimentProgress, index_hasClearedThreshold as hasClearedThreshold, index_inferQuestionType as inferQuestionType, index_isConcluded as isConcluded, index_isNonEvidence as isNonEvidence, index_migrateRegister as migrateRegister, index_missingCompletenessSlots as missingCompletenessSlots, index_needsReview as needsReview, index_portfolioProgress as portfolioProgress, index_rankNextMoves as rankNextMoves, index_readingStrength as readingStrength, index_risk as risk, index_riskThresholdForStage as riskThresholdForStage, index_round2 as round2, index_scoreAndDedupe as scoreAndDedupe, index_sign as sign, index_sourceQuality as sourceQuality, index_w0ForRung as w0ForRung };
|
|
1004
|
+
export { type index_AssembleJourneyInput as AssembleJourneyInput, type index_Attribution as Attribution, type index_AttributionReadingInput as AttributionReadingInput, type index_BarLineInput as BarLineInput, type index_BeliefRisk as BeliefRisk, type index_BeliefStage as BeliefStage, type index_BeliefStageInput as BeliefStageInput, index_COMMITMENT_FOUND as COMMITMENT_FOUND, index_COMPLETENESS_SLOTS as COMPLETENESS_SLOTS, index_CONFIDENCE_FLOOR_BY_STAGE as CONFIDENCE_FLOOR_BY_STAGE, type index_CompletenessInput as CompletenessInput, type index_CompletenessSlot as CompletenessSlot, type index_ConfSign as ConfSign, type index_ConfidenceReadingInput as ConfidenceReadingInput, index_DEFAULT_QUESTION_TYPE as DEFAULT_QUESTION_TYPE, type index_ExperimentConfidenceBarInput as ExperimentConfidenceBarInput, type index_ExperimentConfidenceReadingInput as ExperimentConfidenceReadingInput, index_INFERABLE_QUESTION_TYPES as INFERABLE_QUESTION_TYPES, type index_ImpactAssumptionInput as ImpactAssumptionInput, type index_JourneyBeliefInput as JourneyBeliefInput, type index_JourneyEvent as JourneyEvent, type index_JourneyEventKind as JourneyEventKind, type index_JourneyExperimentInput as JourneyExperimentInput, index_KILL_LANE_THRESHOLD as KILL_LANE_THRESHOLD, index_MAX_STRENGTH as MAX_STRENGTH, type index_MigratedAssumption as MigratedAssumption, type index_MigratedReading as MigratedReading, type index_MigrationAssumption as MigrationAssumption, type index_MigrationReading as MigrationReading, type index_MigrationResult as MigrationResult, type index_MoveKind as MoveKind, type index_Mover as Mover, type index_MoverKind as MoverKind, type index_NextMove as NextMove, type index_NextMoveAssumptionInput as NextMoveAssumptionInput, type index_NextMoveDecisionInput as NextMoveDecisionInput, type index_NextMoveExperimentInput as NextMoveExperimentInput, type index_NextMoveInput as NextMoveInput, type index_PortfolioBeliefInput as PortfolioBeliefInput, type index_PortfolioProgress as PortfolioProgress, type index_Progress as Progress, index_RISK_THRESHOLD_BY_STAGE as RISK_THRESHOLD_BY_STAGE, index_RUNG_ANCHOR as RUNG_ANCHOR, type index_Scored as Scored, type index_StageExperimentInput as StageExperimentInput, type index_StageKey as StageKey, type index_StrengthInput as StrengthInput, type index_TestMeter as TestMeter, type index_TrajectoryPoint as TrajectoryPoint, index_W0 as W0, index_W0_BY_RUNG as W0_BY_RUNG, index_assembleJourney as assembleJourney, index_assumptionComplete as assumptionComplete, index_assumptionCompleteness as assumptionCompleteness, index_beliefRisk as beliefRisk, index_beliefTestMeters as beliefTestMeters, index_ceilingAnchor as ceilingAnchor, index_classifyStage as classifyStage, index_commitmentFactor as commitmentFactor, index_completenessSlotPresence as completenessSlotPresence, index_confidence as confidence, index_confidenceAttribution as confidenceAttribution, index_confidenceFloorForStage as confidenceFloorForStage, index_confidenceTrajectory as confidenceTrajectory, index_deriveBeliefStage as deriveBeliefStage, index_derivedImpacts as derivedImpacts, index_emptyTestMeter as emptyTestMeter, index_experimentConfidence as experimentConfidence, index_experimentProgress as experimentProgress, index_hasClearedThreshold as hasClearedThreshold, index_inferQuestionType as inferQuestionType, index_isConcluded as isConcluded, index_isNonEvidence as isNonEvidence, index_migrateRegister as migrateRegister, index_missingCompletenessSlots as missingCompletenessSlots, index_needsReview as needsReview, index_portfolioProgress as portfolioProgress, index_rankNextMoves as rankNextMoves, index_readingStrength as readingStrength, index_risk as risk, index_riskThresholdForStage as riskThresholdForStage, index_round2 as round2, index_scoreAndDedupe as scoreAndDedupe, index_sign as sign, index_sourceQuality as sourceQuality, index_w0ForRung as w0ForRung };
|
|
975
1005
|
}
|
|
976
1006
|
|
|
977
|
-
export { type
|
|
1007
|
+
export { type TestMeter as $, type AttributionReadingInput as A, type BarLineInput as B, COMPLETENESS_SLOTS as C, DEFAULT_QUESTION_TYPE as D, type ExperimentConfidenceBarInput as E, type MigrationResult as F, type MoveKind as G, type Mover as H, INFERABLE_QUESTION_TYPES as I, type JourneyBeliefInput as J, KILL_LANE_THRESHOLD as K, type MoverKind as L, MAX_STRENGTH as M, type NextMove as N, type NextMoveAssumptionInput as O, type NextMoveDecisionInput as P, type NextMoveExperimentInput as Q, type NextMoveInput as R, type PortfolioBeliefInput as S, type PortfolioProgress as T, type Progress as U, RISK_THRESHOLD_BY_STAGE as V, RUNG_ANCHOR as W, type Scored as X, type StageExperimentInput as Y, type StageKey as Z, type StrengthInput as _, type CompletenessSlot as a, type TrajectoryPoint as a0, W0 as a1, W0_BY_RUNG as a2, assembleJourney as a3, beliefRisk as a4, beliefTestMeters as a5, ceilingAnchor as a6, classifyStage as a7, commitmentFactor as a8, completenessSlotPresence as a9, confidence as aa, confidenceAttribution as ab, confidenceFloorForStage as ac, confidenceTrajectory as ad, deriveBeliefStage as ae, derivedImpacts as af, emptyTestMeter as ag, experimentConfidence as ah, experimentProgress as ai, hasClearedThreshold as aj, inferQuestionType as ak, isConcluded as al, isNonEvidence as am, migrateRegister as an, needsReview as ao, portfolioProgress as ap, rankNextMoves as aq, risk as ar, riskThresholdForStage as as, round2 as at, scoreAndDedupe as au, sign as av, w0ForRung as aw, type CompletenessInput as b, assumptionCompleteness as c, assumptionComplete as d, type AssembleJourneyInput as e, type Attribution as f, type BeliefRisk as g, type BeliefStage as h, index as i, type BeliefStageInput as j, COMMITMENT_FOUND as k, CONFIDENCE_FLOOR_BY_STAGE as l, missingCompletenessSlots as m, type ConfSign as n, type ConfidenceReadingInput as o, type ExperimentConfidenceReadingInput as p, type ImpactAssumptionInput as q, readingStrength as r, sourceQuality as s, type JourneyEvent as t, type JourneyEventKind as u, type JourneyExperimentInput as v, type MigratedAssumption as w, type MigratedReading as x, type MigrationAssumption as y, type MigrationReading as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { R as Relation, C as Collection, A as AssumptionRecord, a as ReadingRecord, D as DecisionRecord, b as AssumptionDerived, E as ExperimentRecord, c as ExperimentDerived, d as AnyRecord } from './types-DZIQEO4D.js';
|
|
2
2
|
export { e as AssumptionStatus, B as BarLine, f as BaseRecord, g as BeliefScore, h as DecisionStatus, i as ExperimentStatus, F as Feasibility, G as GlossaryAvoid, j as GlossaryRecord, k as GlossaryStatus, M as MARKET_RUNG_VALUES, l as MagnitudeBand, m as MarketRung, Q as QUESTION_TYPES, n as QuestionType, o as REGISTERS, p as RecordRef, q as Register, r as Result, s as Rung, S as STAGES, t as SourceQualityPick, u as Stage, T as TESTING_RUNGS, v as TestingRung } from './types-DZIQEO4D.js';
|
|
3
3
|
export { D as DataProvider, N as NotFoundError, S as StaleVersionError, i as isNotFoundError, a as isStaleVersionError } from './provider-BAip4ABo.js';
|
|
4
|
-
import { A as AttributionReadingInput } from './index-
|
|
5
|
-
export { C as ASSUMPTION_PRESENCE_SLOTS, a as AssumptionPresenceSlot, b as CompletenessInput, c as assumptionCompleteness, d as assumptionPresenceComplete, i as derivation, m as missingPresenceSlots, s as recomputeSourceQuality, r as recomputeStrength } from './index-
|
|
4
|
+
import { A as AttributionReadingInput } from './index-DhQyc0Gj.js';
|
|
5
|
+
export { C as ASSUMPTION_PRESENCE_SLOTS, a as AssumptionPresenceSlot, b as CompletenessInput, c as assumptionCompleteness, d as assumptionPresenceComplete, i as derivation, m as missingPresenceSlots, s as recomputeSourceQuality, r as recomputeStrength } from './index-DhQyc0Gj.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Relation config — the single table describing, for each linkable relation,
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/derivation/index.ts","../src/derivation/round.ts","../src/derivation/rung.ts","../src/derivation/completeness.ts","../src/derivation/strength.ts","../src/derivation/question-type.ts","../src/derivation/migrate.ts","../src/derivation/source-quality.ts","../src/types.ts","../src/derivation/confidence.ts","../src/derivation/attribution.ts","../src/derivation/progress.ts","../src/derivation/experiment-confidence.ts","../src/derivation/trajectory.ts","../src/derivation/impact.ts","../src/derivation/risk.ts","../src/derivation/portfolio.ts","../src/derivation/next-move.ts","../src/derivation/stage.ts","../src/derivation/journey.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 the instance's `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 isNonEvidence,\n ceilingAnchor,\n RISK_THRESHOLD_BY_STAGE,\n riskThresholdForStage,\n hasClearedThreshold,\n} from \"./rung.js\";\nexport {\n COMPLETENESS_SLOTS,\n completenessSlotPresence,\n missingCompletenessSlots,\n assumptionCompleteness,\n assumptionComplete,\n} from \"./completeness.js\";\nexport type { CompletenessSlot, CompletenessInput } from \"./completeness.js\";\nexport { sign, isConcluded, readingStrength } from \"./strength.js\";\nexport type { StrengthInput } from \"./strength.js\";\nexport {\n inferQuestionType,\n needsReview,\n INFERABLE_QUESTION_TYPES,\n DEFAULT_QUESTION_TYPE,\n} from \"./question-type.js\";\nexport { migrateRegister } from \"./migrate.js\";\nexport type {\n MigrationAssumption,\n MigrationReading,\n MigratedAssumption,\n MigratedReading,\n MigrationResult,\n} from \"./migrate.js\";\nexport { sourceQuality } from \"./source-quality.js\";\nexport {\n confidence,\n scoreAndDedupe,\n commitmentFactor,\n COMMITMENT_FOUND,\n W0,\n W0_BY_RUNG,\n w0ForRung,\n} from \"./confidence.js\";\nexport type { ConfidenceReadingInput, Scored } from \"./confidence.js\";\nexport { confidenceAttribution } from \"./attribution.js\";\nexport type {\n Attribution,\n AttributionReadingInput,\n Mover,\n MoverKind,\n} from \"./attribution.js\";\nexport { experimentProgress } from \"./progress.js\";\nexport type { BarLineInput, Progress } from \"./progress.js\";\nexport { experimentConfidence, MAX_STRENGTH } from \"./experiment-confidence.js\";\nexport type {\n ExperimentConfidenceBarInput,\n ExperimentConfidenceReadingInput,\n} from \"./experiment-confidence.js\";\nexport { confidenceTrajectory } from \"./trajectory.js\";\nexport type { TrajectoryPoint } from \"./trajectory.js\";\nexport { derivedImpacts } from \"./impact.js\";\nexport type { ImpactAssumptionInput } from \"./impact.js\";\nexport { risk } from \"./risk.js\";\nexport { beliefRisk, portfolioProgress } from \"./portfolio.js\";\nexport type {\n BeliefRisk,\n PortfolioBeliefInput,\n PortfolioProgress,\n} from \"./portfolio.js\";\nexport { rankNextMoves, KILL_LANE_THRESHOLD } from \"./next-move.js\";\nexport type {\n MoveKind,\n NextMove,\n NextMoveAssumptionInput,\n NextMoveExperimentInput,\n NextMoveDecisionInput,\n NextMoveInput,\n} from \"./next-move.js\";\nexport {\n beliefTestMeters,\n classifyStage,\n deriveBeliefStage,\n emptyTestMeter,\n} from \"./stage.js\";\nexport type {\n BeliefStage,\n BeliefStageInput,\n ConfSign,\n StageExperimentInput,\n StageKey,\n TestMeter,\n} from \"./stage.js\";\nexport { assembleJourney } from \"./journey.js\";\nexport type {\n AssembleJourneyInput,\n JourneyBeliefInput,\n JourneyEvent,\n JourneyEventKind,\n JourneyExperimentInput,\n} from \"./journey.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 * The question-type-aware evidence ladder (DEV-5890).\n *\n * Source of truth: `skills/_shared/ontology.yaml` → `vocabularies.rung` +\n * `vocabularies.question_type`. A rung is an evidence TYPE; magnitude band\n * (Low/Typical/High) is the intensity within a type. The band applies to\n * EVERY rung, so every rung looks up its anchor through the 3D table\n * `RUNG_ANCHOR[questionType][rung][band]`.\n *\n * Seven sub-ladders, one per question type. Evidence types that are\n * **non-evidence** for a question type carry anchor `0` across all bands —\n * they contribute `s=0` and are flagged at the UI/skill layer (not a write\n * blocker). The `0` is structural, not a separate flag.\n *\n * The rung vocabulary itself (`Talk`, `Desk research`, `Signed up`,\n * `Observed usage`, `Signed intent`, `Paying users`) is unchanged across\n * all sub-ladders — the same six rungs exist in every sub-ladder, with\n * different anchors (including `0` for non-evidence). This preserves the\n * existing `Rung` type and all reading-row machinery.\n *\n * Research backing: EBM GRADE (evidence hierarchies are question-relative),\n * Bayesian confirmation theory (probative value is hypothesis-relative),\n * qualitative research methods (qual is the ceiling for mechanism/existence\n * questions; saturation is a validity criterion), revealed preference\n * (sustained usage is the cleanest utility signal; stated intent is\n * non-evidence for WTP). See `docs/question-types.md`.\n *\n * W0 stays keyed by evidence type (within a question type), not by stage or\n * question type — see `confidence.ts` → `W0_BY_RUNG`. The learning rate\n * tracks the instrument's reliability, not the stakes or the claim kind.\n * Empirical-Bayes per-question-type W0 tuning is flagged as v2 (out of scope).\n */\nimport type { MagnitudeBand, QuestionType, Rung, Stage } from \"../types.js\";\n\n/**\n * The 3D anchor table — `RUNG_ANCHOR[questionType][rung][band]`.\n *\n * Seven sub-ladders. `0` entries are the non-evidence set for that question\n * type (the reading contributes `s=0`, flagged at the UI/skill layer). The\n * shape — what's probative, what's the ceiling, what's non-evidence — is\n * the decision; the exact values are illustrative anchors calibrated against\n * the research backing (see `docs/question-types.md`).\n *\n * Per the spec table:\n *\n * | Question Type | Talk L/T/H | Desk L/T/H | Signed up L/T/H | Observed usage L/T/H | Signed intent L/T/H | Paying users L/T/H | Ceiling |\n * |-------------------|--------------|--------------|-----------------|----------------------|----------------------|--------------------|-----------------------|\n * | Existence | 10/20/30 | 15/15/15 | 0/0/0 | 20/35/50 | 0/0/0 | 0/0/0 | Observed usage High(50)|\n * | Prevalence | 0/0/0 | 15/15/15 | 0/0/0 | 25/40/50 | 0/0/0 | 0/0/0 | Observed usage High(50)|\n * | CausalEffect | 0/0/0 | 0/0/0 | 0/0/0 | 30/50/70 | 30/50/70 | 50/70/90 | Paying users High(90) |\n * | WillingnessToPay | 0/0/0 | 0/0/0 | 30/50/70 | 0/0/0 | 50/70/85 | 75/88/99 | Paying users High(99) |\n * | ValueUtility | 10/20/30 | 0/0/0 | 0/0/0 | 30/50/70 | 0/0/0 | 0/0/0 | Observed usage High(70)|\n * | Regulatory | 0/0/0 | 30/50/70 | 0/0/0 | 0/0/0 | 0/0/0 | 0/0/0 | Desk research High(70)|\n * | Feasibility | 0/0/0 | 15/15/15 | 0/0/0 | 30/50/70 | 0/0/0 | 0/0/0 | Observed usage High(70)|\n */\ntype BandRecord = Record<MagnitudeBand, number>;\ntype RungAnchors = Record<Rung, BandRecord>;\n\nconst Z = { Low: 0, Typical: 0, High: 0 } as const satisfies BandRecord;\n\nfunction band(low: number, typical: number, high: number): BandRecord {\n return { Low: low, Typical: typical, High: high };\n}\n\n/** The seven sub-ladders, keyed by question type. */\nexport const RUNG_ANCHOR: Record<QuestionType, RungAnchors> = {\n Existence: {\n Talk: band(10, 20, 30),\n \"Desk research\": band(15, 15, 15),\n \"Signed up\": Z,\n \"Observed usage\": band(20, 35, 50),\n \"Signed intent\": Z,\n \"Paying users\": Z,\n },\n Prevalence: {\n Talk: Z,\n \"Desk research\": band(15, 15, 15),\n \"Signed up\": Z,\n \"Observed usage\": band(25, 40, 50),\n \"Signed intent\": Z,\n \"Paying users\": Z,\n },\n CausalEffect: {\n Talk: Z,\n \"Desk research\": Z,\n \"Signed up\": Z,\n \"Observed usage\": band(30, 50, 70),\n \"Signed intent\": band(30, 50, 70),\n \"Paying users\": band(50, 70, 90),\n },\n WillingnessToPay: {\n Talk: Z,\n \"Desk research\": Z,\n \"Signed up\": band(30, 50, 70),\n \"Observed usage\": Z,\n \"Signed intent\": band(50, 70, 85),\n \"Paying users\": band(75, 88, 99),\n },\n ValueUtility: {\n Talk: band(10, 20, 30),\n \"Desk research\": Z,\n \"Signed up\": Z,\n \"Observed usage\": band(30, 50, 70),\n \"Signed intent\": Z,\n \"Paying users\": Z,\n },\n Regulatory: {\n Talk: Z,\n \"Desk research\": band(30, 50, 70),\n \"Signed up\": Z,\n \"Observed usage\": Z,\n \"Signed intent\": Z,\n \"Paying users\": Z,\n },\n Feasibility: {\n Talk: Z,\n \"Desk research\": band(15, 15, 15),\n \"Signed up\": Z,\n \"Observed usage\": band(30, 50, 70),\n \"Signed intent\": Z,\n \"Paying users\": Z,\n },\n};\n\n/**\n * Is a rung **non-evidence** for a question type? A derived predicate — the\n * reading is allowed (not a write blocker) but contributes `s=0` and is\n * flagged at the UI/skill layer for human review (\"reclassify the assumption\n * or drop the reading\"). Non-evidence is `0` anchor across all bands, by\n * construction.\n */\nexport function isNonEvidence(\n questionType: QuestionType,\n rung: Rung,\n): boolean {\n const anchors = RUNG_ANCHOR[questionType]?.[rung];\n if (!anchors) return false;\n return anchors.Low === 0 && anchors.Typical === 0 && anchors.High === 0;\n}\n\n/**\n * The ceiling anchor for a (question type × rung) — the strongest band\n * (`High`). Used by the dashboard to label the ceiling on each sub-ladder and\n * by `/experiment-design` to size the next test against the question type's\n * ceiling.\n */\nexport function ceilingAnchor(\n questionType: QuestionType,\n rung: Rung,\n): number {\n return RUNG_ANCHOR[questionType]?.[rung]?.High ?? 0;\n}\n\n/**\n * The Risk threshold below which an assumption is \"validated enough\" for its\n * stage (DEV-5890). Pragmatic encroachment + Bezos two-way vs one-way doors;\n * Stage is the reversibility proxy. The threshold does NOT flip a status —\n * Live assumptions stay Live and ranked forever (`docs/validated.md`). It is\n * consumed by the dashboard's test-next surface, the `/assumptions audit`\n * skill, and the `/experiment-design` skill. It does NOT enter the\n * Confidence formula.\n */\nexport const RISK_THRESHOLD_BY_STAGE: Record<Stage, number> = {\n Discovery: 30, // two-way door — act on weak evidence\n Validation: 15, // becoming one-way — need more before committing\n Scale: 10, // one-way door — strong evidence before scaling\n Maturity: 5, // defensive, often regulatory — strongest evidence\n};\n\n/**\n * The Risk threshold for a given stage — the stopping rule for attention.\n * Falls back to the tightest threshold (Maturity) when the stage is absent,\n * so a missing stage never silently lowers the bar.\n */\nexport function riskThresholdForStage(stage: Stage | null | undefined): number {\n if (stage && stage in RISK_THRESHOLD_BY_STAGE) {\n return RISK_THRESHOLD_BY_STAGE[stage];\n }\n return RISK_THRESHOLD_BY_STAGE.Maturity;\n}\n\n/**\n * Has the assumption cleared its stage's Risk threshold? \"Cleared\" = Risk is\n * at or below the threshold — the belief has enough evidence for its stage's\n * stopping bar. \"Needs evidence\" = above the threshold — testing-priority.\n */\nexport function hasClearedThreshold(\n risk: number,\n stage: Stage | null | undefined,\n): boolean {\n return risk <= riskThresholdForStage(stage);\n}","/**\n * Assumption completeness — a derived readiness meter, never stored.\n *\n * Replaces the old Gaps / presence-field machinery (OPS-1305): an assumption's\n * Draft-vs-Live readiness is the structural presence of its slots, not a set of\n * hand-maintained tags. `Completeness %` is `filled slots / all slots`; a\n * fully-filled assumption reads 100 and is Live-ready, an empty draft reads low.\n *\n * Slots (each an equal sixth):\n * Description · Lens · Impact · Scoring justification · Dependencies traced\n * · Question Type\n *\n * Question Type (DEV-5890) is the 6th slot: an assumption without a Question\n * Type has `Completeness %` < 100 and cannot go Live. The grill infers the\n * Question Type from the falsification test and confirms with the user\n * (the gaming guard).\n *\n * Pure, no backend dependency — the same function the recompute pass stamps\n * into the derived tuple and the audit checks readiness against.\n */\n\n/** The structural slots whose presence makes an assumption Live-ready. */\nexport const COMPLETENESS_SLOTS = [\n \"Description\",\n \"Lens\",\n \"Impact\",\n \"Scoring justification\",\n \"Dependencies traced\",\n \"Question Type\",\n] as const;\n\nexport type CompletenessSlot = (typeof COMPLETENESS_SLOTS)[number];\n\n/** The record shape completeness reads — an assumption's structural fields. */\nexport interface CompletenessInput {\n Description?: unknown;\n Lens?: unknown;\n Impact?: unknown;\n \"Scoring justification\"?: unknown;\n dependsOnIds?: unknown;\n enablesIds?: unknown;\n \"Question Type\"?: unknown;\n}\n\n/** A text slot is present only when it is a non-blank string. */\nfunction hasText(value: unknown): boolean {\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\n/** Impact is present when it is a real finite number (0 is a valid score). */\nfunction hasNumber(value: unknown): boolean {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\n/** Dependencies are traced once at least one Depends on / Enables link exists. */\nfunction hasAny(value: unknown): boolean {\n return Array.isArray(value) && value.length > 0;\n}\n\n/** Whether each slot is structurally present. */\nexport function completenessSlotPresence(\n record: CompletenessInput,\n): Record<CompletenessSlot, boolean> {\n return {\n Description: hasText(record.Description),\n Lens: hasText(record.Lens),\n Impact: hasNumber(record.Impact),\n \"Scoring justification\": hasText(record[\"Scoring justification\"]),\n \"Dependencies traced\":\n hasAny(record.dependsOnIds) || hasAny(record.enablesIds),\n \"Question Type\": hasText(record[\"Question Type\"]),\n };\n}\n\n/** The slots that are absent or blank on a record. */\nexport function missingCompletenessSlots(\n record: CompletenessInput,\n): CompletenessSlot[] {\n const present = completenessSlotPresence(record);\n return COMPLETENESS_SLOTS.filter((slot) => !present[slot]);\n}\n\n/** Completeness as a whole-number percentage (0, 20, 40, 60, 80, 100). */\nexport function assumptionCompleteness(record: CompletenessInput): number {\n const present = completenessSlotPresence(record);\n const filled = COMPLETENESS_SLOTS.filter((slot) => present[slot]).length;\n return Math.round((filled / COMPLETENESS_SLOTS.length) * 100);\n}\n\n/** True when every slot is present — the structural precondition to be Live. */\nexport function assumptionComplete(record: CompletenessInput): boolean {\n return missingCompletenessSlots(record).length === 0;\n}\n","/**\n * Strength — the signed reading value `s` the Confidence average reads.\n *\n * Formula (`ontology.yaml` → `derivations.strength`):\n * `RUNG_ANCHOR[questionType][rung][band] × sign(Result)`\n * — Validated positive, Invalidated negative; 0 unless Validated/Invalidated.\n *\n * The anchor lookup is 3D (DEV-5890): keyed by the linked assumption's\n * Question Type, the reading's Rung, and the row's Magnitude band. A rung\n * that is non-evidence for the assumption's question type carries anchor 0\n * across all bands, so the reading contributes `s=0` and is flagged at the\n * UI/skill layer (not a write blocker). `isNonEvidence(questionType, rung)`\n * is derived, never stored.\n *\n * Every rung carries a magnitude band (0.14); the lookup is the same for\n * testing and market rungs.\n */\nimport type { MagnitudeBand, QuestionType, Result, Rung } from \"../types.js\";\nimport { RUNG_ANCHOR } 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\n/** A reading counts toward Confidence only once concluded either way; an\n * Inconclusive reading carries no signal. The single definition the whole\n * derivation module (and its record→input mappers) share. */\nexport function isConcluded(result: Result): boolean {\n return result === \"Validated\" || result === \"Invalidated\";\n}\n\nexport interface StrengthInput {\n /** The linked assumption's question type — sets the anchor sub-ladder. */\n questionType: QuestionType;\n rung: Rung;\n result: Result;\n /** Magnitude band; defaults to \"Typical\" when absent. Applies to ALL rungs. */\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 const band = input.magnitudeBand ?? \"Typical\";\n return (RUNG_ANCHOR[input.questionType]?.[input.rung]?.[band] ?? 0) * s;\n}","/**\n * Question-type inference — the falsification-test rule (DEV-5890).\n *\n * The question type is set by what would prove the assumption WRONG, not by\n * what evidence is cheap to gather. \"Users will pay $50/mo\" is falsified by\n * offering it and watching them not pay → WillingnessToPay, regardless of how\n * many interview quotes exist. The function is conservative: if the\n * falsification test is ambiguous, it returns `Existence` (the most\n * permissive sub-ladder) and flags for human review. The migration script\n * runs this over all assumptions and produces a review queue for the ones it\n * isn't confident on.\n *\n * The grill enforces the gaming guard: the inferred type (from the\n * falsification bar) must match the stated type, or the assumption is rejected.\n * This is the rule that stops a team reframing \"will users pay?\" as \"do users\n * express willingness to pay?\" (existence question, qual ceiling) to avoid\n * running a market test.\n *\n * Rules (matched in order; first match wins):\n *\n * - Falsified by \"the regulation prohibits / the regulator rules against\n * / the compliance audit fails\" → Regulatory\n * - Falsified by \"they don't pay / don't sign up / don't commit / fake-door\n * rate below X%\" → WillingnessToPay\n * - Falsified by \"they stop using it / drop-off exceeds X / sustained usage\n * falls below / retention\" → ValueUtility\n * - Falsified by \"the treatment group doesn't differ from control / no\n * causal effect / variant doesn't outperform\" → CausalEffect\n * - Falsified by \"the rate is below X% / fewer than N of N / the proportion\n * is below / the share is below\" → Prevalence\n * - Falsified by \"they can't complete the flow / the system can't do X /\n * can't be built\" → Feasibility\n * - Falsified by \"no one reports this pain / no one describes this\n * mechanism / no one reports caring\" → Existence\n * - Otherwise (ambiguous) → Existence (flagged for human review)\n *\n * Regulatory is matched first because a regulator ruling against is a\n * distinctive falsification that shouldn't be misread as a willingness-to-pay\n * or feasibility claim. WillingnessToPay precedes ValueUtility because \"they\n * pay\" is the WTP falsification and \"they stop using\" is the ValueUtility\n * falsification — the keywords are distinct but WTP is the more specific claim\n * kind. CausalEffect precedes Prevalence because \"rate below X%\" appears in\n * both prevalence bars and A/B conversion-rate bars; the A/B framing\n * (\"treatment group\", \"control\", \"variant\", \"outperform\") is matched first.\n *\n * The function is a pure string-matching heuristic — no NLP, no external\n * calls. The migration's review queue is the backstop for cases the\n * heuristic misses.\n */\nimport type { QuestionType } from \"../types.js\";\n\n/** The default question type for an ambiguous falsification test — the most\n * permissive sub-ladder, and the migration's flagged-for-review default. */\nexport const DEFAULT_QUESTION_TYPE: QuestionType = \"Existence\";\n\ninterface Rule {\n readonly type: QuestionType;\n readonly patterns: readonly RegExp[];\n}\n\n// Lowercase match; word-boundary-tolerant. Patterns are intentionally specific\n// so a WTP bar (\"they don't pay\") isn't misread as Existence (\"no one reports\n// they pay\"). Order matters — see the module doc.\nconst RULES: readonly Rule[] = [\n {\n type: \"Regulatory\",\n patterns: [\n /\\bregulat(?:or|ion)\\s+(?:prohibits|rul(?:es|ed)\\s+against)\\b/,\n /\\bregulator\\b.*\\bagainst\\b/,\n /\\bcompliance\\s+audit\\s+fails?\\b/,\n /\\bprohibits?\\b.*\\bscoring\\b/,\n ],\n },\n {\n type: \"WillingnessToPay\",\n patterns: [\n /\\b(?:don't|do not|won't|will not)\\s+pay\\b/,\n /\\bfake[-\\s]?door\\s+(?:signup\\s+)?rate\\s+below\\b/,\n /\\bfake[-\\s]?door\\b.*\\brate\\b/,\n /\\bdon't\\s+sign\\s+up\\b/,\n /\\bno\\s+(?:buyer|user)s?\\s+(?:sign|pay|commit)\\b/,\n /\\bsigns?\\s+(?:an\\s+)?loi\\b/,\n /\\bput(?:s|ting)?\\s+down\\s+(?:a\\s+)?deposit\\b/,\n /\\bdon't\\s+commit\\b/,\n /\\boffered\\s+users?\\s+pay\\b/,\n /\\bpay(?:ing)?\\s+(?:rate|share)?\\s*(?:is\\s+)?below\\b/,\n /\\bfewer\\s+than\\s+\\d+\\s+of\\s+\\d+\\s+offered\\s+users?\\s+pay\\b/,\n ],\n },\n {\n type: \"ValueUtility\",\n patterns: [\n /\\bstop\\s+using\\s+(?:it|the)\\b/,\n /\\bdrop[-\\s]?off\\s+exceeds\\b/,\n /\\bsustained\\s+usage\\s+falls?\\s+below\\b/,\n /\\bretention\\s+(?:drops?|falls?)\\s+below\\b/,\n /\\bweek-\\d+\\s+retention\\s+drops?\\s+below\\b/,\n /\\bdisuse\\b/,\n ],\n },\n {\n type: \"CausalEffect\",\n patterns: [\n /\\btreatment\\s+group(?:'s)?\\s+doesn'?t\\s+differ\\s+from\\s+control\\b/,\n /\\bno\\s+causal\\s+effect\\s+of\\b/,\n /\\bvariant\\s+doesn'?t\\s+outperform\\s+control\\b/,\n /\\bdiffer\\s+from\\s+control\\b/,\n /\\boutperform\\s+control\\b/,\n /\\btreatment\\s+vs\\.?\\s+control\\b/,\n ],\n },\n {\n type: \"Prevalence\",\n patterns: [\n /\\brate\\s+is\\s+below\\b/,\n /\\bfewer\\s+than\\s+\\d+\\s+of\\s+\\d+\\s+(?:interviewed|surveyed|of)\\b/,\n /\\bfewer\\s+than\\s+\\d+%\\s+of\\s+\\w+\\s+hit\\b/,\n /\\bthe\\s+(?:rate|share|proportion)\\s+is\\s+below\\b/,\n /\\b(?:in\\s+)?surveyed\\s+teams\\b/,\n /\\bshare\\s+(?:of\\s+\\w+\\s+)?(?:is\\s+)?below\\b/,\n /\\bproportion\\s+is\\s+below\\b/,\n ],\n },\n {\n type: \"Feasibility\",\n patterns: [\n /\\bcan'?t\\s+complete\\s+the\\s+flow\\b/,\n /\\bsystem\\s+can'?t\\s+(?:do|ingest|handle|build)\\b/,\n /\\bcan'?t\\s+be\\s+built\\b/,\n /\\bcan'?t\\s+ingest\\b/,\n /\\bprototype\\s+can'?t\\s+be\\s+built\\b/,\n /\\bcan'?t\\s+\\w+\\s+(?:unaided|without\\s+(?:help|regressions))\\b/,\n ],\n },\n {\n type: \"Existence\",\n patterns: [\n /\\bno\\s+one\\s+(?:we\\s+interview\\s+)?(?:describes|reports|mentions)\\b/,\n /\\bno\\s+interviewee(?:s)?\\s+(?:describe|report|mention)s?\\b/,\n /\\bcan'?t\\s+describe\\s+the\\s+mechanism\\b/,\n /\\bno\\s+one\\s+reports\\s+caring\\b/,\n /\\bno\\s+one\\s+describes\\b/,\n ],\n },\n];\n\n/** Normalize a string for matching: lowercase, collapse whitespace. */\nfunction normalize(s: string): string {\n return s.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\n\n/**\n * Infer the Question Type from an assumption's description and falsification\n * bar. The falsification bar (`wrongIfBar`) carries the signal — the\n * description is only consulted as a fallback context. Returns the most\n * permissive type (`Existence`) for an ambiguous bar; the migration's review\n * queue is the backstop.\n */\nexport function inferQuestionType(\n description: string,\n wrongIfBar: string,\n): QuestionType {\n const bar = normalize(wrongIfBar ?? \"\");\n const desc = normalize(description ?? \"\");\n const hay = bar.length > 0 ? `${bar} ${desc}` : desc;\n\n for (const rule of RULES) {\n for (const re of rule.patterns) {\n if (re.test(hay)) return rule.type;\n }\n }\n return DEFAULT_QUESTION_TYPE;\n}\n\n/** The set of question types the inference rule can return. Exported for\n * the migration's \"covered types\" assertion. */\nexport const INFERABLE_QUESTION_TYPES: readonly QuestionType[] = [\n \"Existence\",\n \"Prevalence\",\n \"CausalEffect\",\n \"WillingnessToPay\",\n \"ValueUtility\",\n \"Regulatory\",\n \"Feasibility\",\n] as const;\n\n/** Whether an inferred question type should be flagged for human review.\n * Conservative: only the confident matches are clean; the default\n * (Existence-from-ambiguous) is always flagged. A match is \"confident\" if the\n * bar matched a rule other than the Existence fallback. */\nexport function needsReview(\n description: string,\n wrongIfBar: string,\n inferred: QuestionType,\n): boolean {\n if (wrongIfBar == null || wrongIfBar.trim() === \"\") return true;\n // Re-run the inference; if the bar was empty/ambiguous (no rule matched the\n // bar text), the inferred type is the fallback and should be reviewed.\n const bar = normalize(wrongIfBar);\n for (const rule of RULES) {\n if (rule.type === inferred) {\n for (const re of rule.patterns) {\n if (re.test(bar)) return false;\n }\n }\n }\n return true;\n}","/**\n * Migration entry point — DEV-5890.\n *\n * Takes the OLD-shape register (assumptions without `Question Type`, readings\n * with stale Strength) and produces the new shape: each assumption with\n * `Question Type` filled via `inferQuestionType`, each reading's `s`\n * recomputed via the 3D `RUNG_ANCHOR[questionType][rung][band]` lookup, each\n * reading flagged if its rung is non-evidence for the linked assumption's\n * question type (keeping its would-have-been `s` visible for diffing), and a\n * summary report of Confidence deltas + flag counts.\n *\n * The migration is reversible: the entry point writes a backup of the\n * pre-migration state to `doshi-validation-os/docs/migration/backups/`. Roll\n * back restores from the backup.\n *\n * Pure — no I/O. The caller (the migration script) reads the register, calls\n * `migrateRegister`, and writes the result. Tested at the highest seam with a\n * fixture covering existence, WTP, causal, and regulatory cases.\n */\nimport {\n confidence,\n inferQuestionType,\n isNonEvidence,\n needsReview,\n readingStrength,\n RISK_THRESHOLD_BY_STAGE,\n riskThresholdForStage,\n RUNG_ANCHOR,\n type ConfidenceReadingInput,\n} from \"./index.js\";\nimport type {\n AnyRecord,\n AssumptionRecord,\n QuestionType,\n ReadingRecord,\n Result,\n Rung,\n} from \"../types.js\";\n\n/** A minimal assumption shape for migration input. */\nexport interface MigrationAssumption {\n id: string;\n Description?: string;\n /** The falsification test — the \"we're wrong if\" bar from the grill. */\n wrongIfBar?: string;\n /** The existing question type, if any (otherwise inferred). */\n \"Question Type\"?: QuestionType | null;\n /** Existing derived Confidence (for the delta report). */\n derived?: { confidence?: number } | null;\n /** The assumption's Stage, for the threshold comparison in the summary. */\n Stage?: string | null;\n Impact?: number | null;\n moot?: boolean;\n dependsOnIds?: string[];\n enablesIds?: string[];\n}\n\n/** A minimal reading shape for migration input. */\nexport interface MigrationReading {\n id: string;\n /** The assumption id this reading's belief scores (single-belief migration). */\n assumptionId: string;\n Rung: Rung;\n Result: Result;\n magnitudeBand?: \"Low\" | \"Typical\" | \"High\" | null;\n Source: string | null;\n Representativeness: number;\n Credibility: number;\n Date: string | null;\n experimentId: string | null;\n /** Existing derived Strength (for the delta report). */\n derived?: { strength?: number } | null;\n}\n\n/** The output shape for a migrated assumption. */\nexport interface MigratedAssumption extends MigrationAssumption {\n \"Question Type\": QuestionType;\n /** Whether the inferred question type was flagged for human review. */\n questionTypeReviewNeeded: boolean;\n /** The new derived Confidence (recomputed under the new sub-ladder). */\n newConfidence: number;\n /** The pre-migration Confidence (for the delta report). */\n oldConfidence: number;\n /** The Confidence delta (new − old). */\n confidenceDelta: number;\n /** The stage's Risk threshold (for the summary). */\n riskThreshold: number;\n}\n\n/** The output shape for a migrated reading's belief. */\nexport interface MigratedReading {\n id: string;\n assumptionId: string;\n Rung: Rung;\n /** The recomputed Strength (s) under the new sub-ladder. */\n newStrength: number;\n /** The pre-migration Strength (for diffing). */\n oldStrength: number;\n /** True if the rung is non-evidence for the linked assumption's question type. */\n nonEvidence: boolean;\n}\n\n/** The full migration result. */\nexport interface MigrationResult {\n assumptions: MigratedAssumption[];\n readings: MigratedReading[];\n /** The number of readings flagged as non-evidence. */\n nonEvidenceFlagCount: number;\n /** The number of assumptions flagged for human review of their question type. */\n reviewQueueCount: number;\n /** Per-assumption Confidence deltas, sorted by |delta| descending. */\n confidenceDeltas: { id: string; delta: number; oldConfidence: number; newConfidence: number }[];\n /** Per-assumption ranking shifts in the Risk-ranked register (oldRank →\n * newRank, 1-indexed; positive = moved down the ranking, negative = moved\n * up). Sorted by |shift| descending. */\n rankingShifts: { id: string; oldRank: number; newRank: number; shift: number }[];\n}\n\n/**\n * Migrate an old-shape register to the new question-type-aware shape. Pure —\n * no I/O. The caller reads the register, calls this, and writes the result;\n * the backup is the caller's responsibility (write a dated copy of the input\n * before calling `migrateRegister`).\n *\n * The migration:\n * 1. Infers `Question Type` for each assumption from its falsification bar.\n * 2. Recomputes each reading's `s` via `RUNG_ANCHOR[questionType][rung][band]`.\n * 3. Flags readings whose rung is non-evidence for the linked assumption's\n * question type (keeps the would-have-been `s` visible for diffing).\n * 4. Recomputes each assumption's Confidence under the new sub-ladder.\n * 5. Produces a summary report of Confidence deltas and flag counts.\n */\nexport function migrateRegister(\n assumptions: MigrationAssumption[],\n readings: MigrationReading[],\n): MigrationResult {\n // 1. Infer Question Type for each assumption; build the id → type map.\n const questionTypeById = new Map<string, QuestionType>();\n const reviewNeededById = new Set<string>();\n for (const a of assumptions) {\n const existing = a[\"Question Type\"];\n let qt: QuestionType;\n let review = false;\n if (existing && ([\"Existence\", \"Prevalence\", \"CausalEffect\", \"WillingnessToPay\", \"ValueUtility\", \"Regulatory\", \"Feasibility\"] as const).includes(existing)) {\n // An existing valid question type is kept; still check whether the bar\n // matches (the gaming guard — infer from the bar and flag a mismatch).\n qt = existing;\n // No review when the question type was explicitly set.\n } else {\n qt = inferQuestionType(a.Description ?? \"\", a.wrongIfBar ?? \"\");\n review = needsReview(a.Description ?? \"\", a.wrongIfBar ?? \"\", qt);\n if (review) reviewNeededById.add(a.id);\n }\n questionTypeById.set(a.id, qt);\n }\n\n // 2. Recompute each reading's Strength under the new sub-ladder.\n const migratedReadings: MigratedReading[] = [];\n let nonEvidenceFlagCount = 0;\n for (const r of readings) {\n const questionType = questionTypeById.get(r.assumptionId) ?? \"Existence\";\n const band = r.magnitudeBand ?? \"Typical\";\n const newStrength = readingStrength({\n questionType,\n rung: r.Rung,\n result: r.Result,\n magnitudeBand: band,\n });\n // The would-have-been Strength under the OLD single-ladder (DEV-5880 /\n // pre-DEV-5890 anchors). We approximate by the legacy anchor for the rung\n // (the pre-DEV-5890 single-ladder Typical values) × sign. This is a\n // best-effort diff; the caller can supply the actual old Strength via\n // `r.derived.strength` when available.\n const oldStrength = r.derived?.strength ?? 0;\n const nonEvidence = isNonEvidence(questionType, r.Rung);\n if (nonEvidence) nonEvidenceFlagCount += 1;\n migratedReadings.push({\n id: r.id,\n assumptionId: r.assumptionId,\n Rung: r.Rung,\n newStrength,\n oldStrength,\n nonEvidence,\n });\n }\n\n // 3. Recompute each assumption's Confidence under the new sub-ladder.\n // Group the readings into ConfidenceReadingInput by assumption, threading\n // the inferred question type.\n const inputsByAssumption = new Map<string, ConfidenceReadingInput[]>();\n for (const a of assumptions) inputsByAssumption.set(a.id, []);\n for (const r of readings) {\n const questionType = questionTypeById.get(r.assumptionId) ?? \"Existence\";\n inputsByAssumption.get(r.assumptionId)?.push({\n id: r.id,\n source: r.Source,\n rung: r.Rung,\n result: r.Result,\n questionType,\n representativeness: r.Representativeness,\n credibility: r.Credibility,\n date: r.Date,\n magnitudeBand: r.magnitudeBand ?? undefined,\n experimentId: r.experimentId,\n });\n }\n\n const migratedAssumptions: MigratedAssumption[] = assumptions.map((a) => {\n const questionType = questionTypeById.get(a.id) ?? \"Existence\";\n const oldConfidence = a.derived?.confidence ?? 0;\n const newConfidence = confidence(inputsByAssumption.get(a.id) ?? []);\n const stage = (a.Stage as keyof typeof RISK_THRESHOLD_BY_STAGE | null) ?? null;\n return {\n ...a,\n \"Question Type\": questionType,\n questionTypeReviewNeeded: reviewNeededById.has(a.id),\n newConfidence,\n oldConfidence,\n confidenceDelta: Math.round((newConfidence - oldConfidence) * 100) / 100,\n riskThreshold: riskThresholdForStage(stage as never),\n };\n });\n\n const confidenceDeltas = migratedAssumptions\n .map((a) => ({\n id: a.id,\n delta: a.confidenceDelta,\n oldConfidence: a.oldConfidence,\n newConfidence: a.newConfidence,\n }))\n .sort((a, b) => Math.abs(b.delta) - Math.abs(a.delta));\n\n // Ranking shift: rank assumptions by (oldConfidence desc, id) and\n // (newConfidence desc, id), then compute the rank delta per assumption.\n // 1-indexed; positive shift = moved DOWN the ranking (less confident),\n // negative = moved UP (more confident).\n const oldRanked = [...assumptions]\n .map((a) => ({ id: a.id, c: a.derived?.confidence ?? 0 }))\n .sort((a, b) => b.c - a.c || a.id.localeCompare(b.id));\n const newRanked = [...migratedAssumptions]\n .map((a) => ({ id: a.id, c: a.newConfidence }))\n .sort((a, b) => b.c - a.c || a.id.localeCompare(b.id));\n const oldRank = new Map(oldRanked.map((r, i) => [r.id, i + 1]));\n const newRank = new Map(newRanked.map((r, i) => [r.id, i + 1]));\n const rankingShifts = migratedAssumptions\n .map((a) => {\n const o = oldRank.get(a.id) ?? 0;\n const n = newRank.get(a.id) ?? 0;\n return { id: a.id, oldRank: o, newRank: n, shift: n - o };\n })\n .filter((r) => r.oldRank > 0 && r.newRank > 0)\n .sort((a, b) => Math.abs(b.shift) - Math.abs(a.shift));\n\n return {\n assumptions: migratedAssumptions,\n readings: migratedReadings,\n nonEvidenceFlagCount,\n reviewQueueCount: reviewNeededById.size,\n confidenceDeltas,\n rankingShifts,\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 * 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/**\n * The five registers. `goals` was unified into `experiments` and the `people`\n * reference collection retired (OPS-1305): Owner / Agreed by now reference a\n * dashboard user (the auth-sourced team list), not a register row.\n */\nexport const REGISTERS = [\n \"assumptions\",\n \"experiments\",\n \"readings\",\n \"decisions\",\n \"glossary\",\n] as const;\nexport type Register = (typeof REGISTERS)[number];\n\n/** There is no reference collection any more — a collection is a register. */\nexport type Collection = Register;\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\";\n/**\n * The unified evidence-plan lifecycle (OPS-1305). `Draft` is the new gate a\n * commit clears (Draft→Running); conclude+verdict stays the Running→Closed\n * gate. Absorbs what the retired Goal record's status used to carry.\n */\nexport type ExperimentStatus = \"Draft\" | \"Running\" | \"Closed\" | \"Archived\";\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/**\n * The question-type-aware evidence ladder (DEV-5890). A rung is an evidence\n * TYPE; magnitude band (Low/Typical/High) is the intensity *within* a type.\n * The band applies to EVERY rung, so every rung looks up its anchor through\n * `RUNG_ANCHOR[questionType][rung][band]` — the anchor (ceiling `s`) is per\n * (question type × rung × band). A rung that is **non-evidence** for a\n * question type carries anchor 0 across all bands (contributes `s=0`,\n * flagged at the UI/skill layer — not a write blocker).\n *\n * Talk: 3 / 6 / 10 (Opinion / Pitch-deck / Anecdotal merged)\n * Desk research: 15 / 15 / 15 (flat — desk research has no meaningful bands,\n * but the field exists for uniformity)\n * Signed up: 30 / 50 / 70 (consumer lens's first do-rung)\n * Observed usage: 30 / 50 / 70 (consumer lens; was Prototype usage + Survey\n * at scale, now collapsed)\n * Signed intent: 30 / 50 / 70 (commercial/investor lens)\n * Paying users: 30 / 50 / 70 (commercial/investor lens)\n *\n * The above anchors are the LEGACY single-ladder values; the live anchors are\n * per question type — see `derivation/rung.ts` for the 3D table. The rung\n * vocabulary itself is fixed across all question types; only the anchors\n * (including `0` for non-evidence) vary.\n *\n * The lens determines which \"do\" rungs are available; Talk + Desk work for any\n * lens. The rung-to-lens mapping is a grading guideline, not a schema\n * constraint — any Rung can appear on any assumption.\n */\nexport const TESTING_RUNGS = [\n \"Talk\",\n \"Desk research\",\n \"Signed up\",\n \"Observed usage\",\n] as const;\nexport const MARKET_RUNG_VALUES = [\"Signed intent\", \"Paying users\"] as const;\nexport type TestingRung = (typeof TESTING_RUNGS)[number];\nexport type MarketRung = (typeof MARKET_RUNG_VALUES)[number];\nexport type Rung = TestingRung | MarketRung;\n\n/**\n * The four discovery stages (docs/stage-policy.md). The stored value is the\n * name; the ordinal 1–4 is for sorting only. Stage sets the Risk threshold\n * for action (the stopping rule, `RISK_THRESHOLD_BY_STAGE`) — it does NOT\n * enter the Confidence formula. Orthogonal to Question Type (kind of claim)\n * and Lens (who the actor is).\n */\nexport const STAGES = [\"Discovery\", \"Validation\", \"Scale\", \"Maturity\"] as const;\nexport type Stage = (typeof STAGES)[number];\n\n/**\n * The seven question types (docs/question-types.md). The kind of CLAIM an\n * assumption raises — set by the falsification-test rule (what would prove\n * the assumption wrong), not by what evidence is cheap. The question type\n * determines which rungs are probative, what the ceiling is, and which rungs\n * are non-evidence (anchor 0) — via `RUNG_ANCHOR[questionType][rung][band]`.\n * Orthogonal to Stage (kind of response) and Lens (who the actor is).\n */\nexport const QUESTION_TYPES = [\n \"Existence\",\n \"Prevalence\",\n \"CausalEffect\",\n \"WillingnessToPay\",\n \"ValueUtility\",\n \"Regulatory\",\n \"Feasibility\",\n] as const;\nexport type QuestionType = (typeof QUESTION_TYPES)[number];\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 derived numbers stored on an assumption (never hand-typed). */\nexport interface AssumptionDerived {\n derivedImpact: number;\n risk: number;\n confidence: number;\n /** Structural readiness meter, 0–100 (see `derivation/completeness.ts`). */\n completeness: number;\n}\n\nexport interface AssumptionRecord extends BaseRecord {\n Title: string;\n Description: string;\n Lens: string | null;\n /** The kind of external-actor response this belief tests (Discovery /\n * Validation / Scale / Maturity). Sets the Risk threshold for action. */\n Stage: Stage | null;\n /** The kind of claim this belief raises (Existence / Prevalence / ...).\n * Determines which rungs are probative, the ceiling, and non-evidence —\n * via `RUNG_ANCHOR[questionType][rung][band]`. */\n \"Question Type\": QuestionType | 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 /** Reference to a dashboard user (auth team list), not a `people` row. */\n Owner: string[];\n moot: boolean;\n /**\n * Kept, narrowed to the Impact-seed rationale (OPS-1305): why the seed\n * `Impact` was scored as it was, incl. dated moot lines. The `5 Whys`,\n * `Metric for truth`, and `Gaps` fields are gone — the why-trace lives in the\n * `Depends on / Enables` chain and the audit check-types are transient grill\n * stages, not stored tags. Readiness is the derived `completeness`.\n */\n \"Scoring justification\": string;\n dependsOnIds: string[];\n enablesIds: string[];\n contradictsIds: string[];\n readingIds: string[];\n derived: AssumptionDerived;\n}\n\n/**\n * One belief's scoring inside a reading — the per-assumption verdict an\n * artifact carries. A reading (one artifact ROW) may score several beliefs at\n * once. The Rung is a property of the artifact, so it (and the market magnitude\n * band) lives on the row, ONE per artifact (OPS 0.10): a reading is at a single\n * evidence rung and reads for/against each belief at that rung. Only the Result\n * and its rationale vary per belief. `strength` is derived (row rung × sign of\n * this belief's Result), never hand-typed.\n */\nexport interface BeliefScore {\n assumptionId: string;\n Result: Result;\n /** The rationale for this belief's Result at the row's rung. */\n \"Grading justification\": string;\n /** The verbatim quote or observed fact this belief was graded from. */\n excerpt?: string;\n /** Derived per belief: row rung anchor × sign(Result) [× row magnitude band]. */\n derived: { strength: number };\n /** Provenance: the original reading id this belief was migrated from. */\n sourceReadingId?: string;\n /** Optional review caveat, e.g. a second-hand-credibility note. */\n reviewNote?: string;\n}\n\nexport interface ReadingRecord extends BaseRecord {\n Title: string;\n /** The independence/dedupe key — the generator (person / dataset / cohort). */\n Source: string | null;\n /**\n * Provenance links (recording, dashboard, CRM row, user id) — 0..N, drives no\n * math and never keys dedupe (OPS-1305). Split out from `Source` so the\n * dedupe key stays narrow.\n */\n contextLinks: string[];\n /** The originating plan, or null for a bare/found reading (no Goal origin). */\n experimentId: string | null;\n /** The evidence rung — one per artifact; every belief is scored at it (OPS 0.10). */\n Rung: Rung;\n /** For a Market-rung reading: the magnitude band from the absolute outcome. */\n magnitudeBand?: MagnitudeBand;\n Representativeness: SourceQualityPick;\n Credibility: SourceQualityPick;\n Date: string | null;\n Owner: string[];\n /** Free-text narrative of the reading. */\n body?: string;\n /** Per-belief scores — one artifact row can score several beliefs at once. */\n beliefs: BeliefScore[];\n /** Convenience projection of `beliefs[].assumptionId`; kept in sync on write. */\n assumptionIds: string[];\n derived: { sourceQuality: number };\n}\n\n/**\n * A pre-registered bar line — the per-belief pass/fail test on an experiment,\n * stored as an embedded array on the experiment (no identity of its own; see\n * `connectors/nosql-schema.md`). `barVerdict` is set once at closure and is a\n * report only — never folded into Confidence.\n */\nexport interface BarLine {\n assumptionId: string;\n rightIf: string;\n wrongIf?: string | null;\n plannedRung: Rung;\n barVerdict?: Result | null;\n}\n\n/**\n * The derived numbers stored on an experiment (never hand-typed).\n * `experimentConfidence` is the [0, 100] confidence gauge (50 = neutral),\n * coverage-gated and verdict-aligned; see `derivation/experiment-confidence.ts`.\n */\nexport interface ExperimentDerived {\n experimentConfidence: number;\n}\n\nexport interface ExperimentRecord extends BaseRecord {\n Title: string;\n Instrument: string | null;\n Feasibility: Feasibility | null;\n Status: ExperimentStatus;\n /** Free-text narrative of the plan. */\n body?: string;\n closureReason: \"Completed\" | \"Early-stop\" | \"Kill\" | null;\n /** Optional deadline a committed plan carries (folded in from the Goal). */\n Deadline: string | null;\n /** Terminal closure outcome, null until Closed (folded in from the Goal). */\n Outcome: \"Achieved\" | \"Missed\" | \"Dropped\" | null;\n /** Reference to a dashboard user, not a `people` row. */\n Owner: string[];\n Date: string | null;\n /** The embedded pre-registered bar lines; drives progress-to-conclusion. */\n barLines: BarLine[];\n /** Convenience projection: the assumptions the bar lines test. */\n barLineAssumptionIds: string[];\n /** Derived numbers — recomputed on every touching write. */\n derived?: ExperimentDerived;\n}\n\nexport interface DecisionRecord extends BaseRecord {\n Title: string;\n Status: DecisionStatus;\n /** The one-line statement of what was decided (promoted from the body). */\n Statement: string;\n /** Why the Unanimity score was scored as it was (promoted from the body). */\n \"Unanimity justification\": string;\n /** References to dashboard users (auth team list), not `people` rows. */\n Owner: string[];\n \"Agreed by\": string[];\n basedOnIds: string[];\n resolvesIds: string[];\n}\n\n/** One structured \"don't say\" entry on a glossary term. */\nexport interface GlossaryAvoid {\n audience: string;\n phrase: string;\n fix: string;\n}\n\nexport interface GlossaryRecord extends BaseRecord {\n Title: string;\n Status: GlossaryStatus;\n /** All properties, no body (OPS-1305): the terminology check parses these. */\n Definition: string;\n Avoid: GlossaryAvoid[];\n \"How it differs\": string;\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 | \"decision-based-on\"\n | \"decision-resolves\";\n","/**\n * Confidence — signed −100…100, 0 = no evidence.\n *\n * Formula (`ontology.yaml` → `derivations.confidence`):\n * (Σ wi·si) / (Σ_rung W0[rung] + Σ wi), per-rung W0,\n * wi = |si| × Source quality × commitment, si = the reading's signed Strength,\n * commitment = 1.0 for an experiment-linked reading, 0.85 for a found one.\n *\n * W0 is per-rung: each rung has its own prior weight controlling how many\n * distinct sources it takes to approach that rung's anchor (cap). Desk\n * research has a low W0 (2 — one authoritative source nearly saturates);\n * talk rungs have a higher W0 (6.5 — needs ~10 readings to approach the\n * cap); do-rungs (Survey/Prototype/Signed/Paying) have high W0s (~120-410\n * — needs ~20 readings to reach 75% of cap). When readings span multiple\n * rungs, each rung with evidence contributes its W0 to the denominator\n * once (one prior per evidence stream).\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). Market-rung readings never dedupe (each closed commitment\n * is its own unit). No corroboration bump.\n */\nimport type { MagnitudeBand, QuestionType, Result, Rung } from \"../types.js\";\nimport { MARKET_RUNG_VALUES } from \"../types.js\";\nimport { round2 } from \"./round.js\";\nimport { sourceQuality } from \"./source-quality.js\";\nimport { isConcluded, readingStrength } from \"./strength.js\";\n\n/** Market rungs never dedupe (each closed commitment is its own unit). */\nconst MARKET_RUNG_SET = new Set<Rung>(MARKET_RUNG_VALUES);\nfunction isMarketRung(rung: Rung): boolean {\n return MARKET_RUNG_SET.has(rung);\n}\n\n/**\n * Per-rung prior weight — controls how many distinct sources approach the\n * rung's anchor. Tuned so: Desk 2 readings → ~90% of cap; talk 10 readings →\n * ~90% of cap; do-rungs 20 readings → ~75% of cap. See\n * `docs/evidence-ladder.md` for the derivation.\n */\nexport const W0_BY_RUNG: Record<Rung, number> = {\n // Talk — 10 readings → ~90% of cap.\n Talk: 6.5,\n // Desk research — 2 readings → ~90% of cap (authoritative, rare).\n \"Desk research\": 2,\n // All do-rungs — 20 readings → ~75% of cap. Equal ceilings (70) and equal\n // W0s across consumer (Signed up / Observed usage) and commercial\n // (Signed intent / Paying users) lenses.\n \"Signed up\": 327,\n \"Observed usage\": 327,\n \"Signed intent\": 327,\n \"Paying users\": 327,\n};\n\n/**\n * The neutral prior weight for a rung — how much evidence at this rung is\n * needed to overcome the prior. Per-rung (not a flat constant): see\n * {@link W0_BY_RUNG}.\n */\nexport function w0ForRung(rung: Rung): number {\n return W0_BY_RUNG[rung] ?? 100;\n}\n\n/**\n * The legacy flat W0 — retained for backwards compatibility only. New code\n * should use {@link w0ForRung}. Equal to the old default of 100.\n * @deprecated Use {@link w0ForRung} for per-rung priors.\n */\nexport const W0 = 100;\n\n/**\n * Commitment factor for a *found* reading — one with no originating experiment.\n * A pre-registered (experiment-linked) reading weighs at full commitment (1.0);\n * a found reading is discounted to this. It is a SMALL tiebreaker: it scales the\n * weight only, never the Strength, so it can never reorder readings across rungs\n * (\"Rung dominates\").\n */\nexport const COMMITMENT_FOUND = 0.85;\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 /** The linked assumption's question type — sets the anchor sub-ladder. */\n questionType: QuestionType;\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 * The originating experiment, or null/undefined for a *found* reading. Drives\n * the commitment factor in the weight (found → {@link COMMITMENT_FOUND}).\n */\n experimentId?: string | null;\n}\n\n/** The commitment weighting for a reading: full for committed, discounted for found. */\nexport function commitmentFactor(experimentId: string | null | undefined): number {\n return experimentId ? 1.0 : COMMITMENT_FOUND;\n}\n\nexport interface Scored {\n input: ConfidenceReadingInput;\n strength: number;\n sq: number;\n /** The reading's weight in the average: |strength| × Source quality × commitment. */\n weight: number;\n}\n\n/**\n * Score every concluded reading and resolve the Source dedupe — the shared\n * front half of the Confidence average. `confidence()` reduces the winners to\n * a number; `confidenceAttribution()` reuses the same winners so the movers it\n * reports always decompose the very number the drawer shows. Market rungs never\n * dedupe (each closed commitment is its own unit).\n */\nexport function scoreAndDedupe(readings: ConfidenceReadingInput[]): Scored[] {\n const scored: Scored[] = readings\n .filter((r) => isConcluded(r.result))\n .map((r) => {\n const strength = readingStrength(r);\n const sq = sourceQuality(r.representativeness, r.credibility);\n const weight = Math.abs(strength) * sq * commitmentFactor(r.experimentId);\n return { input: r, strength, sq, weight };\n })\n .filter((x) => x.strength !== 0);\n\n const best = new Map<string, Scored>();\n for (const x of scored) {\n if (isMarketRung(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 return [...best.values()];\n}\n\nexport function confidence(readings: ConfidenceReadingInput[]): number {\n const winners = scoreAndDedupe(readings);\n // Per-rung prior: sum W0 once for each rung that has ≥1 concluded reading.\n // This preserves each rung's accumulation behaviour independently — desk\n // stays fast (low W0), talk stays slow (high W0) — and mixed-rung evidence\n // gets each rung's prior added once (one prior per evidence stream).\n const rungsPresent = new Set(winners.map((x) => x.input.rung));\n let den = 0;\n for (const rung of rungsPresent) den += w0ForRung(rung);\n let num = 0;\n for (const x of winners) {\n num += x.weight * x.strength;\n den += x.weight;\n }\n return den > 0 ? round2(num / den) : 0;\n}\n","/**\n * Confidence attribution — the \"what's moving the number\" half of the\n * understanding layer (OPS-1276). Decomposes an assumption's Confidence into\n * the experiments (and direct readings) contributing to it, ranked by how hard\n * each pushes the number up or down.\n *\n * A winner's contribution is its signed share of the average:\n * cᵢ = (weightᵢ · strengthᵢ) / den,\n * den = Σ_rung W0[rung] + Σ weight (one prior per rung with evidence)\n * so Σ contributions = Σ(wᵢ·sᵢ)/den = Confidence (the W0·0 prior term is 0).\n * The reveal therefore literally adds up to the hero number. Contributions are\n * grouped by the reading's experiment (experiment-less readings — bare/found —\n * fall into a \"direct\" bucket), then ranked by |contribution|. A reading's\n * origin is an experiment or nothing; the retired Goal container is gone\n * (OPS-1305).\n */\nimport { w0ForRung, scoreAndDedupe, type ConfidenceReadingInput } from \"./confidence.js\";\nimport { round2 } from \"./round.js\";\n\n/**\n * The reading shape the attribution reveal consumes. `experimentId` (the\n * grouping key, and the commitment-factor driver) lives on\n * {@link ConfidenceReadingInput}; this alias marks the attribution call sites.\n */\nexport type AttributionReadingInput = ConfidenceReadingInput;\n\n/** What a mover is anchored to — an experiment, or nothing (direct). */\nexport type MoverKind = \"experiment\" | \"direct\";\n\nexport interface Mover {\n /** Stable grouping key: the experiment id, or \"direct\". */\n key: string;\n kind: MoverKind;\n /** The experiment id when `kind === \"experiment\"`, else null. */\n experimentId: string | null;\n /** Signed push on Confidence; the whole set sums to `confidence`. */\n contribution: number;\n /** |contribution| — the rank key and the \"how hard\" magnitude. */\n magnitude: number;\n /** How many (deduped) readings back this mover. */\n readingCount: number;\n /** The winning readings' ids, for drill-through. */\n readingIds: string[];\n}\n\nexport interface Attribution {\n /** The same Confidence the derived box shows (from the same winners). */\n confidence: number;\n /** Movers ranked by |contribution|, strongest first. */\n movers: Mover[];\n}\n\nfunction bucketOf(r: AttributionReadingInput): {\n key: string;\n kind: MoverKind;\n experimentId: string | null;\n} {\n if (r.experimentId) {\n return {\n key: r.experimentId,\n kind: \"experiment\",\n experimentId: r.experimentId,\n };\n }\n return { key: \"direct\", kind: \"direct\", experimentId: null };\n}\n\nexport function confidenceAttribution(\n readings: AttributionReadingInput[],\n): Attribution {\n const winners = scoreAndDedupe(readings);\n // Per-rung prior: sum W0 once for each rung with ≥1 concluded reading,\n // matching the `confidence()` formula.\n const rungsPresent = new Set(winners.map((x) => x.input.rung));\n let den = 0;\n for (const rung of rungsPresent) den += w0ForRung(rung);\n for (const x of winners) den += x.weight;\n\n const byKey = new Map<string, Mover>();\n let num = 0;\n for (const x of winners) {\n num += x.weight * x.strength;\n const b = bucketOf(x.input as AttributionReadingInput);\n const contribution = (x.weight * x.strength) / den;\n const cur = byKey.get(b.key);\n if (cur) {\n cur.contribution += contribution;\n cur.readingCount += 1;\n cur.readingIds.push(x.input.id);\n } else {\n byKey.set(b.key, {\n key: b.key,\n kind: b.kind,\n experimentId: b.experimentId,\n contribution,\n magnitude: 0, // finalised below\n readingCount: 1,\n readingIds: [x.input.id],\n });\n }\n }\n\n const movers = [...byKey.values()].map((m) => ({\n ...m,\n contribution: round2(m.contribution),\n magnitude: round2(Math.abs(m.contribution)),\n }));\n movers.sort((a, b) => b.magnitude - a.magnitude);\n\n return {\n confidence: den > 0 ? round2(num / den) : 0,\n movers,\n };\n}\n","/**\n * Progress-to-conclusion — how close a running experiment is to concluding\n * (OPS-1276). An experiment concludes when every pre-registered bar line has\n * been given a Bar verdict; progress is the count of settled bars against the\n * total. Bar verdict is a report, never a Confidence input (nosql-schema.md),\n * so reading it here is exactly its intended use.\n */\nexport interface BarLineInput {\n /** null/\"\" until the bar is judged at closure. */\n barVerdict?: string | null;\n}\n\nexport interface Progress {\n /** Pre-registered bar lines on the experiment. */\n total: number;\n /** Bars that have a verdict. */\n settled: number;\n /** Bars still awaiting a verdict. */\n toGo: number;\n /** True once every bar is settled (and there is at least one). */\n concluded: boolean;\n}\n\nexport function experimentProgress(bars: BarLineInput[]): Progress {\n const total = bars.length;\n const settled = bars.filter(\n (b) => typeof b.barVerdict === \"string\" && b.barVerdict.trim() !== \"\",\n ).length;\n return {\n total,\n settled,\n toGo: total - settled,\n concluded: total > 0 && settled === total,\n };\n}\n","/**\n * Experiment confidence — a derived [0, 100] gauge for an evidence plan (0.14).\n *\n * Formula (`experimentConfidence`):\n * clamp(50 + 50 × C × S + 5 × A, 0, 100)\n *\n * Where:\n * B = number of bar lines\n * coveredLines = bar lines with ≥1 concluded reading (Validated/Invalidated)\n * C = |coveredLines| / max(B, 1) — coverage factor ∈ [0, 1]\n * For each concluded reading linked to the experiment (deduped by Source,\n * same rule as `confidence.ts`):\n * fᵢ = (strengthᵢ × sqᵢ) / MAX_STRENGTH (signed)\n * strengthᵢ = readingStrength({ rung, result, magnitudeBand })\n * sqᵢ = sourceQuality(representativeness, credibility)\n * F = Σ fᵢ (signed, after dedupe)\n * S = F / (1 + |F|) — soft squash, ℝ → (−1, +1)\n * A = Σ alignedⱼ / B — verdict alignment, per bar\n * alignedⱼ = +1 if barVerdict agrees with the reading sign on bar j,\n * −1 if it contradicts,\n * 0 if Inconclusive or no readings on bar j\n *\n * Neutral = 50 (no evidence yet). Validated readings fill up, Invalidated\n * pull down. Coverage-gated (no readings → C=0 → 50). The 5×A term is a small\n * verdict-alignment nudge so a plan whose bars are settled the way the evidence\n * already points inches further in that direction.\n */\nimport type { MagnitudeBand, QuestionType, Result, Rung } from \"../types.js\";\nimport { round2 } from \"./round.js\";\nimport { sourceQuality } from \"./source-quality.js\";\nimport { isConcluded, readingStrength, sign } from \"./strength.js\";\n\n/** The reference maximum strength — the anchor of the highest rung band. */\nexport const MAX_STRENGTH = 99;\n\nexport interface ExperimentConfidenceBarInput {\n assumptionId: string;\n /** Set at closure; null/absent until then. */\n barVerdict?: Result | null;\n}\n\nexport interface ExperimentConfidenceReadingInput {\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 /** The linked assumption's question type — sets the anchor sub-ladder. */\n questionType: QuestionType;\n magnitudeBand?: MagnitudeBand;\n representativeness: number;\n credibility: number;\n /** The assumption (bar line) this reading scores. */\n assumptionId: string;\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n\n/**\n * Source dedupe — same rule as `confidence.ts`: group by Source (fall back to\n * the reading's own id), keep the entry with the largest |strength|. Market\n * rungs never dedupe (each closed commitment is its own unit). Mirrored here so\n * experiment confidence stays pure and self-contained.\n */\nfunction dedupeBySource(\n readings: ExperimentConfidenceReadingInput[],\n): ExperimentConfidenceReadingInput[] {\n const MARKET = new Set<Rung>([\"Signed intent\", \"Paying users\"]);\n const best = new Map<string, ExperimentConfidenceReadingInput>();\n for (const r of readings) {\n if (MARKET.has(r.rung)) {\n best.set(r.id, r);\n continue;\n }\n const key = r.source || r.id;\n const cur = best.get(key);\n const s = Math.abs(readingStrength(r));\n const better =\n !cur ||\n s > Math.abs(readingStrength(cur)) ||\n (s === Math.abs(readingStrength(cur)) && (r.id > cur.id));\n if (better) best.set(key, r);\n }\n return [...best.values()];\n}\n\n/**\n * Compute the experiment-confidence gauge. `bars` is the experiment's bar\n * lines; `readings` is the concluded readings linked to the experiment (the\n * caller filters to the experiment's `barLineAssumptionIds`).\n */\nexport function experimentConfidence(\n bars: ExperimentConfidenceBarInput[],\n readings: ExperimentConfidenceReadingInput[],\n): number {\n const B = bars.length;\n\n // Only concluded readings enter; dedupe by Source.\n const concluded = dedupeBySource(\n readings.filter((r) => isConcluded(r.result)),\n );\n\n // Coverage: bar lines with ≥1 concluded reading.\n const barsWithReadings = new Set(\n concluded.map((r) => r.assumptionId),\n );\n const covered = bars.filter((b) => barsWithReadings.has(b.assumptionId)).length;\n const C = B > 0 ? covered / B : 0;\n\n // F = Σ fᵢ (signed).\n let F = 0;\n for (const r of concluded) {\n const strength = readingStrength(r);\n const sq = sourceQuality(r.representativeness, r.credibility);\n F += (strength * sq) / MAX_STRENGTH;\n }\n const S = F / (1 + Math.abs(F));\n\n // Verdict alignment: per bar, +1 if barVerdict agrees with the net reading\n // sign on that bar, −1 if it contradicts, 0 if Inconclusive or no readings.\n const signByBar = new Map<string, number>();\n for (const r of concluded) {\n const s = sign(r.result);\n signByBar.set(r.assumptionId, (signByBar.get(r.assumptionId) ?? 0) + s);\n }\n let A = 0;\n for (const bar of bars) {\n const net = signByBar.get(bar.assumptionId);\n if (net === undefined || net === 0) continue; // no readings or net-zero\n const v = bar.barVerdict;\n if (v !== \"Validated\" && v !== \"Invalidated\") continue; // Inconclusive / unset\n const barSign = v === \"Validated\" ? 1 : -1;\n A += net * barSign > 0 ? 1 : -1;\n }\n if (B > 0) A /= B;\n\n const raw = 50 + 50 * C * S + 5 * A;\n return round2(clamp(raw, 0, 100));\n}","/**\n * Confidence over time — the story of how the number got where it is\n * (OPS-1276). At each date a concluded reading was dated, we recompute\n * Confidence over every concluded reading up to and including that date, using\n * the very same `confidence()` the derived box uses (so the last point equals\n * the hero number). Undated concluded readings have no place on the timeline\n * but still bear on the belief, so they are treated as always-present — folded\n * into every point — which keeps the final point equal to today's Confidence.\n */\nimport { confidence, type ConfidenceReadingInput } from \"./confidence.js\";\nimport { isConcluded } from \"./strength.js\";\n\nexport interface TrajectoryPoint {\n /** ISO date at which Confidence took this value. */\n date: string;\n confidence: number;\n}\n\nexport function confidenceTrajectory(\n readings: ConfidenceReadingInput[],\n): TrajectoryPoint[] {\n const concluded = readings.filter((r) => isConcluded(r.result));\n const undated = concluded.filter((r) => !r.date);\n const dated = concluded.filter((r) => r.date);\n if (dated.length === 0) return [];\n\n const dates = [...new Set(dated.map((r) => r.date as string))].sort();\n return dates.map((date) => ({\n date,\n // Everything dated on/before this point, plus the always-present undated.\n confidence: confidence([\n ...undated,\n ...dated.filter((r) => (r.date as string) <= date),\n ]),\n }));\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 the instance's `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","/**\n * Portfolio progress — the one *cross-belief* reading (OPS-1293 / OPS-1300).\n *\n * Every other derived number here is per-record (risk/confidence/impact of one\n * belief). This is the whole-set roll-up behind the pipeline's headline: a\n * **burn-up**, \"% of identified risk bought down\" = Risk Retired ÷ Risk-ever-\n * identified across *all* beliefs, resolved ones included.\n *\n * Like `rankNextMoves`, it is a whole-set ordering computed **fresh on read**,\n * not stored — it stays out of the OPS-1251 on-write recompute (it only reads\n * numbers already kept current). Pure and numeric: the dashboard maps records\n * to these inputs (as `understanding.ts` maps readings), so the rule lives\n * once, here.\n *\n * The model (matches the OPS-1296 prototype's self-consistent maths):\n * - **ever-identified** for a belief = the risk it represented at Confidence\n * ≤ 0, i.e. its Derived Impact. A moot row's Derived Impact is pinned to 0,\n * which would erase it from *both* sides of the fraction, so we floor\n * ever-identified at the hand-scored seed Impact — mooting a belief must\n * *retire* its risk, never shrink the denominator (the burn-up's whole\n * point: resolved risk stays counted).\n * - **live** risk = the belief's current Risk, or 0 once resolved (a kill or\n * a moot resolves the uncertainty — that risk is bought down, not carried).\n * - **retired** = ever-identified − live.\n */\nimport { round2 } from \"./round.js\";\n\nexport interface PortfolioBeliefInput {\n id: string;\n /** Derived Impact — the belief's risk at Confidence ≤ 0 (0 when moot). */\n derivedImpact: number;\n /** The hand-scored seed Impact; the ever-identified floor when moot zeroes\n * Derived Impact. Null/absent treated as 0. */\n seedImpact: number | null;\n /** The belief's current live Risk (stored `derived.risk`). */\n risk: number;\n /** Resolved — Invalidated (killed) or moot. Its live risk reads 0 (fully\n * retired), whatever the stored Risk number says. */\n resolved: boolean;\n}\n\n/** One belief's contribution to the burn-up. */\nexport interface BeliefRisk {\n /** Risk-ever-identified — the denominator's per-belief share. */\n identified: number;\n /** Live risk still carried (0 once resolved). */\n live: number;\n /** Risk bought down — the numerator's per-belief share. */\n retired: number;\n}\n\nexport interface PortfolioProgress {\n /** Σ risk-ever-identified — the burn-up denominator. */\n identified: number;\n /** Σ risk bought down — the burn-up numerator. */\n retired: number;\n /** Σ risk still live. */\n live: number;\n /** Retired ÷ identified as a percentage (0 when nothing is identified). */\n percent: number;\n /** Beliefs still in play (not resolved). */\n liveCount: number;\n /** Beliefs resolved (killed or moot). */\n resolvedCount: number;\n}\n\n/** One belief's identified / live / retired risk — the rule in one place. */\nexport function beliefRisk(b: PortfolioBeliefInput): BeliefRisk {\n const seed = b.seedImpact ?? 0;\n const identified = Math.max(b.derivedImpact, seed, 0);\n // Live risk can never exceed what was ever identified — the clamp keeps a\n // stale/over-large stored Risk from making retired go negative.\n const live = b.resolved ? 0 : Math.min(identified, Math.max(0, b.risk));\n return {\n identified: round2(identified),\n live: round2(live),\n retired: round2(identified - live),\n };\n}\n\n/**\n * Roll every belief up into the portfolio burn-up. Pass the *whole* set\n * (resolved rows included) — a filtered slice understates the denominator and\n * makes fresh or retired risk read as backsliding.\n */\nexport function portfolioProgress(\n beliefs: PortfolioBeliefInput[],\n): PortfolioProgress {\n let identified = 0;\n let live = 0;\n let liveCount = 0;\n let resolvedCount = 0;\n for (const b of beliefs) {\n const r = beliefRisk(b);\n identified += r.identified;\n live += r.live;\n if (b.resolved) resolvedCount += 1;\n else liveCount += 1;\n }\n const retired = identified - live;\n return {\n identified: round2(identified),\n retired: round2(retired),\n live: round2(live),\n percent: identified > 0 ? round2((retired / identified) * 100) : 0,\n liveCount,\n resolvedCount,\n };\n}\n","/**\n * Next-move ranking — the front door's single source of truth for \"what should\n * I do next\" (build OPS-1304; placement OPS-1292; action vocabulary OPS-1291).\n *\n * Ranks *beliefs* — Model A: point at one belief, not a heterogeneous triage\n * queue (OPS-1291) — by the method's Feasibility × Risk rule (`docs/method.md`,\n * `ontology.yaml` → `derived_views.next_move`), and names the single act each\n * belief's stage demands. A belief at Confidence ≤ −50 jumps into a distinct\n * kill/re-test lane that sorts above the Feasibility × Risk order regardless of\n * rank — the one place act-urgency beats belief-risk (`derived_views.kill_lane`).\n *\n * Computed fresh on read: a whole-set ordering, so it stays OUT of the OPS-1251\n * on-write recompute — it reads the derived numbers (Risk, Confidence) those\n * writes already keep current. Pure: no I/O, no caching, no weights framework —\n * the enum→multiplier map below IS the formula (OPS-1292: \"no weights /\n * strategies / caching / framework\").\n */\nimport type { Feasibility } from \"../types.js\";\nimport { round2 } from \"./round.js\";\n\n/** The kill/re-test threshold — Confidence at or below this is the kill lane. */\nexport const KILL_LANE_THRESHOLD = -50;\n\n/**\n * The acts the front door can name, one per belief-stage. The front door\n * *names* all of them; only a subset are human step-in forms — the rest are\n * agent-run / off-dashboard (OPS-1294). Which is which is a presentation\n * concern the dashboard owns, not this ranking.\n */\nexport type MoveKind =\n | \"score-impact\" // Framed — the belief isn't weighted yet (Impact unscored)\n | \"design-experiment\" // Planned — no test plans this belief yet\n | \"record-reading\" // Tested — a test is running, evidence still landing\n | \"decide\" // Known — evidence has landed and no decision rests on it\n | \"retest\"; // kill lane — Confidence ≤ −50, jumps the ordering\n\n/**\n * One belief's next move. `move`/`score`/`reason` are the OPS-1292 output\n * contract; the rest is context the front door renders (the risk chip, the kill\n * banner, the step-in adaptation) — every field read from the inputs, nothing\n * new computed here.\n */\nexport interface NextMove {\n /** The act this belief's stage demands. */\n move: MoveKind;\n assumptionId: string;\n /** The belief statement — the hero headline. */\n title: string;\n /** Feasibility × Risk. Kill-lane rows carry their Risk and sort first. */\n score: number;\n /** Plain-language \"why this\" — explains from the inputs, no jargon. */\n reason: string;\n /** The belief's live derived Risk (0–100). */\n risk: number;\n /** The belief's live derived Confidence (signed −100…100). */\n confidence: number;\n /** The feasibility that fed the score; null when no test plans it yet. */\n feasibility: Feasibility | null;\n /** Confidence ≤ −50 — the override lane. */\n killLane: boolean;\n}\n\n/** A belief and its live derived numbers (the ranking's primary input). */\nexport interface NextMoveAssumptionInput {\n id: string;\n title: string;\n /** AssumptionStatus; `Invalidated` rows are already killed and drop out. */\n status: string;\n /** The hand-scored seed (0–100); null means unscored → \"score impact\". */\n impact: number | null;\n /** Mooted beliefs (Impact pinned to 0 by a decision) drop out of ranking. */\n moot: boolean;\n /** Derived Risk (already recomputed on write — read, never recomputed). */\n risk: number;\n /** Derived Confidence (already recomputed on write). */\n confidence: number;\n /** How many concluded (Validated/Invalidated) readings back this belief. */\n concludedReadings: number;\n}\n\n/** An experiment, reduced to what stage + feasibility resolution needs. */\nexport interface NextMoveExperimentInput {\n /** ExperimentStatus — \"Running\" | \"Closed\". */\n status: string;\n feasibility: Feasibility | null;\n /** The assumption ids this experiment's bar lines name. */\n assumptionIds: string[];\n}\n\n/** A decision, reduced to which beliefs it rests on or resolves. */\nexport interface NextMoveDecisionInput {\n /** DecisionStatus; only standing (Active/Provisional) decisions count. */\n status: string;\n /** The assumption ids this decision rests on (`based on`) or resolves. */\n assumptionIds: string[];\n}\n\nexport interface NextMoveInput {\n assumptions: NextMoveAssumptionInput[];\n experiments: NextMoveExperimentInput[];\n decisions: NextMoveDecisionInput[];\n}\n\n/**\n * Feasibility → the score multiplier. Cheaper (more feasible) tests rank\n * higher, so the cheapest honest test of the riskiest belief is on top\n * (`method.md`). A belief with no planned test yet has unknown feasibility →\n * the neutral middle, so Risk decides its place honestly.\n */\nconst FEASIBILITY_WEIGHT: Record<Feasibility, number> = {\n High: 1,\n Medium: 0.6,\n Low: 0.3,\n};\nconst NEUTRAL_FEASIBILITY_WEIGHT = FEASIBILITY_WEIGHT.Medium;\n\nfunction isStanding(decisionStatus: string): boolean {\n return decisionStatus === \"Active\" || decisionStatus === \"Provisional\";\n}\n\n/** Pick the cheapest (highest-weight) feasibility among a belief's tests. */\nfunction bestFeasibility(\n feasibilities: (Feasibility | null)[],\n): Feasibility | null {\n let best: Feasibility | null = null;\n let bestWeight = -1;\n for (const f of feasibilities) {\n if (!f) continue;\n const w = FEASIBILITY_WEIGHT[f];\n if (w > bestWeight) {\n bestWeight = w;\n best = f;\n }\n }\n return best;\n}\n\n/**\n * The act a belief's stage demands (before the kill-lane override).\n *\n * Deliberately NOT the `stage.ts` structural classifier: that answers \"how far\n * along the Framed→Planned→Tested→Known artifacts are\" (framing completeness,\n * bar-line settlement); this answers \"what should the founder do next\" from the\n * evidence state (is it weighted, has evidence concluded, does a test run, does\n * a decision rest on it). Same belief can be structurally `tested` yet owe a\n * `decide` here — the journey rail (stage) and next-move card (act) are two\n * readings of one belief, so they share the test-plan *meter* (`beliefTestMeters`)\n * but not the classifier.\n */\nfunction stageMove(\n a: NextMoveAssumptionInput,\n hasRunningTest: boolean,\n hasAnyTest: boolean,\n hasStandingDecision: boolean,\n): MoveKind | null {\n if (a.impact === null) return \"score-impact\"; // Framed → weight it\n if (a.concludedReadings === 0) {\n // No evidence yet: if a test is running, wait for readings; else plan one.\n return hasRunningTest ? \"record-reading\" : \"design-experiment\";\n }\n // Evidence has landed. If nothing rests on it yet, it's time to decide;\n // once a standing decision does, the belief is resolved and drops out.\n if (!hasStandingDecision) return \"decide\";\n return null;\n}\n\n/** Plain-language \"why this\" — explains the move from the inputs. */\nfunction reasonFor(move: MoveKind, a: NextMoveAssumptionInput): string {\n switch (move) {\n case \"retest\":\n return `Confidence has fallen to ${Math.round(a.confidence)} — the evidence is turning against this belief. Kill it or test it again.`;\n case \"score-impact\":\n return \"This belief isn't weighted yet — score its impact so its risk can rank against the rest.\";\n case \"design-experiment\":\n return `Your riskiest untested belief. Design the cheapest honest test that could move it.`;\n case \"record-reading\":\n return \"A test is running against this belief — evidence is still landing.\";\n case \"decide\":\n return \"The evidence is in and nothing rests on it yet — time to make the call.\";\n }\n}\n\n/**\n * Rank every unresolved belief into its next move (Model A). Returns a plain\n * sorted list, most-pressing first: the kill lane on top (by Risk), then the\n * rest by Feasibility × Risk, tie-broken by the most-negative Confidence\n * (`derived_views.test_next_surface`), then by id for a stable order. The front\n * door takes the head as the hero and the tail as \"On deck\" / manual override.\n */\nexport function rankNextMoves(input: NextMoveInput): NextMove[] {\n const { assumptions, experiments, decisions } = input;\n\n // Which experiments (and their feasibilities/statuses) name each belief.\n const testsByAssumption = new Map<\n string,\n { running: boolean; any: boolean; feasibilities: (Feasibility | null)[] }\n >();\n for (const exp of experiments) {\n const running = exp.status === \"Running\";\n for (const id of exp.assumptionIds) {\n const entry = testsByAssumption.get(id) ?? {\n running: false,\n any: false,\n feasibilities: [],\n };\n entry.any = true;\n entry.running = entry.running || running;\n entry.feasibilities.push(exp.feasibility);\n testsByAssumption.set(id, entry);\n }\n }\n\n // Which beliefs a standing decision rests on or resolves.\n const decidedAssumptions = new Set<string>();\n for (const d of decisions) {\n if (!isStanding(d.status)) continue;\n for (const id of d.assumptionIds) decidedAssumptions.add(id);\n }\n\n const moves: NextMove[] = [];\n for (const a of assumptions) {\n if (a.moot || a.status === \"Invalidated\") continue;\n\n const killLane = a.confidence <= KILL_LANE_THRESHOLD;\n const tests = testsByAssumption.get(a.id);\n const feasibility = bestFeasibility(tests?.feasibilities ?? []);\n\n const move: MoveKind | null = killLane\n ? \"retest\"\n : stageMove(\n a,\n tests?.running ?? false,\n tests?.any ?? false,\n decidedAssumptions.has(a.id),\n );\n if (move === null) continue; // resolved — no move\n\n const weight = feasibility\n ? FEASIBILITY_WEIGHT[feasibility]\n : NEUTRAL_FEASIBILITY_WEIGHT;\n // Kill-lane rows carry their raw Risk so they order among themselves by\n // Risk; the killLane flag (not the score) floats them above everyone else.\n const score = round2(killLane ? a.risk : weight * a.risk);\n\n moves.push({\n move,\n assumptionId: a.id,\n title: a.title,\n score,\n reason: reasonFor(move, a),\n risk: a.risk,\n confidence: a.confidence,\n feasibility,\n killLane,\n });\n }\n\n moves.sort((x, y) => {\n if (x.killLane !== y.killLane) return x.killLane ? -1 : 1;\n if (y.score !== x.score) return y.score - x.score;\n if (x.confidence !== y.confidence) return x.confidence - y.confidence;\n return x.assumptionId < y.assumptionId ? -1 : 1;\n });\n return moves;\n}\n","/**\n * Per-belief stage — where one belief sits on the loop's four-stage spine\n * (Framed → Planned → Tested → Known) and its four meters (OPS-1329).\n *\n * This is the *single-belief* analogue of the pipeline's cross-belief roll-up:\n * the pipeline row-builder used to derive framing / test-plan / test-progress\n * inline, and the front-door move ladder (OPS-1304) re-walked the same stages a\n * second way. The shared classification lives here now, so the pipeline board,\n * the front door, and the per-belief journey rail all read one rule rather than\n * three that can drift.\n *\n * Pure and computed fresh on read (like `portfolio.ts` / `next-move.ts`): it\n * only reads numbers already kept current, so it stays out of the OPS-1251\n * on-write recompute. The record → meter mapping (bar lines, presence fields)\n * lives in the dashboard, as elsewhere; this module takes the reduced inputs.\n */\nimport { KILL_LANE_THRESHOLD } from \"./next-move.js\";\n\n/** The four loop stages a belief travels, in order (OPS-1293). */\nexport type StageKey = \"framed\" | \"planned\" | \"tested\" | \"known\";\n\n/** Sign of a belief's Confidence — the Known meter's direction. */\nexport type ConfSign = \"pos\" | \"neg\" | \"zero\";\n\n/** One belief's test state, aggregated across every experiment aimed at it. */\nexport interface TestMeter {\n /** A bar line (or the convenience projection) names this belief. */\n planned: boolean;\n /** Pre-registered bars that have a verdict. */\n settled: number;\n /** Pre-registered bars in total. */\n total: number;\n}\n\n/** A test's bar lines, reduced to what the meter needs (register-agnostic). */\nexport interface StageExperimentInput {\n /** Each bar line naming a belief, and whether it has settled (has a verdict). */\n bars: { assumptionId: string; settled: boolean }[];\n /** Beliefs this test plans via the convenience projection (bars may be unexpanded). */\n plannedAssumptionIds: string[];\n}\n\n/** The reduced inputs one belief's stage is derived from. */\nexport interface BeliefStageInput {\n /** Framing completeness, 0–100 (`assumptionCompleteness`). */\n framed: number;\n /** Live derived Confidence (signed −100…100). */\n confidence: number;\n /** This belief's aggregated test state. */\n test: TestMeter;\n}\n\n/** One belief's position on the spine plus its four meters. */\nexport interface BeliefStage {\n /** Where the belief sits: framed → planned → tested → known. */\n stage: StageKey;\n /** Meter 1 — framing completeness, 0–100. */\n framed: number;\n /** Meter 2 — a test has been designed against this belief. */\n planned: boolean;\n /** Meter 3 — pre-registered bars settled / total. */\n tested: { settled: number; total: number };\n /** Meter 4 — signed Known: the belief's Confidence. */\n confidence: number;\n /** Sign bucket for the Known gauge direction. */\n confSign: ConfSign;\n /** Confidence ≤ −50 — the kill/re-test overlay (the same lane as the front door). */\n killZone: boolean;\n}\n\n/** An empty test state — a belief no experiment has named yet. */\nexport function emptyTestMeter(): TestMeter {\n return { planned: false, settled: 0, total: 0 };\n}\n\n/**\n * For every belief, the state of the tests aimed at it — whether one is designed\n * and how many of its pre-registered bars have settled, aggregated across all\n * experiments. Factored out of the pipeline row-builder so the board and a\n * single belief's rail agree by construction.\n */\nexport function beliefTestMeters(\n experiments: StageExperimentInput[],\n): Map<string, TestMeter> {\n const byAssumption = new Map<string, TestMeter>();\n const ensure = (id: string): TestMeter => {\n let s = byAssumption.get(id);\n if (!s) {\n s = emptyTestMeter();\n byAssumption.set(id, s);\n }\n return s;\n };\n for (const exp of experiments) {\n for (const b of exp.bars) {\n if (!b.assumptionId) continue;\n const s = ensure(b.assumptionId);\n s.planned = true;\n s.total += 1;\n if (b.settled) s.settled += 1;\n }\n // A designed test may name a belief via the convenience projection even when\n // its bar lines aren't expanded — still counts as Planned.\n for (const id of exp.plannedAssumptionIds) ensure(id).planned = true;\n }\n return byAssumption;\n}\n\n/**\n * Classify a belief on the spine from its meters. The kill-zone overlay is\n * *not* a stage — a belief whose evidence has turned is still structurally\n * wherever its framing/tests put it (a re-test moves it backward via the Known\n * meter, OPS-1300), so this stays pure status.\n */\nexport function classifyStage(framed: number, test: TestMeter): StageKey {\n if (framed < 100) return \"framed\";\n if (!test.planned) return \"planned\";\n if (test.total === 0 || test.settled < test.total) return \"tested\";\n return \"known\";\n}\n\n/** One belief's stage + four meters — the rail's data. */\nexport function deriveBeliefStage(input: BeliefStageInput): BeliefStage {\n const { framed, confidence, test } = input;\n return {\n stage: classifyStage(framed, test),\n framed,\n planned: test.planned,\n tested: { settled: test.settled, total: test.total },\n confidence,\n confSign: confidence > 0 ? \"pos\" : confidence < 0 ? \"neg\" : \"zero\",\n killZone: confidence <= KILL_LANE_THRESHOLD,\n };\n}\n","/**\n * The per-belief journey event log (OPS-1329) — the belief's life ordered into\n * dated events: bet → score → experiment → readings → confidence-cross → now.\n * It is the *story* half of the journey drill-in (the *rail* half is `stage.ts`).\n *\n * No new maths: Confidence at each point is read off `confidenceTrajectory`\n * (the very numbers the understanding layer already shows), and the current\n * number off `confidence()`. Nothing is invented — an event whose underlying\n * datum is absent (no impact score, no experiment, no concluded reading, no\n * kill-zone cross) is simply omitted, and no date is ever faked (an event with\n * no real date carries `date: null` and takes its place by structural order).\n *\n * Pure and label-free: the dashboard journey view-model adds the copy. Computed\n * fresh on read, out of the OPS-1251 on-write recompute.\n */\nimport type { Result } from \"../types.js\";\nimport type { AttributionReadingInput } from \"./attribution.js\";\nimport { confidence } from \"./confidence.js\";\nimport { KILL_LANE_THRESHOLD } from \"./next-move.js\";\nimport { isConcluded } from \"./strength.js\";\nimport { confidenceTrajectory } from \"./trajectory.js\";\n\n/** The kinds of event a belief's life produces, in structural order. */\nexport type JourneyEventKind =\n | \"bet\" // the belief was written\n | \"score\" // its impact was scored\n | \"experiment\" // a test was designed against it\n | \"reading\" // evidence landed\n | \"confidence-cross\" // Confidence fell into the kill zone\n | \"now\"; // the belief's state today\n\n/** The structural order the story walks — the tie-break for undated events. */\nconst KIND_ORDER: Record<JourneyEventKind, number> = {\n bet: 0,\n score: 1,\n experiment: 2,\n reading: 3,\n \"confidence-cross\": 4,\n now: 5,\n};\n\n/** One event in a belief's life. Label-free — the view-model adds the copy. */\nexport interface JourneyEvent {\n kind: JourneyEventKind;\n /** ISO date, or null when no real date exists (never faked). */\n date: string | null;\n /** Confidence known at this event (reading / cross / now); null otherwise. */\n confidence: number | null;\n /** The reading's verdict, for `reading` events; null otherwise. */\n result: Result | null;\n /** The source record id (reading id, experiment id) when the event has one. */\n refId: string | null;\n}\n\n/** The belief itself, reduced to what the log needs. */\nexport interface JourneyBeliefInput {\n /** When the bet was written — the `bet` event's date. */\n createdAt: string | null;\n /** Impact has been scored (a non-null seed) — emits the `score` event. */\n impactScored: boolean;\n}\n\n/** A test aimed at this belief, reduced to what the log needs. */\nexport interface JourneyExperimentInput {\n id: string;\n /** When the test was designed; null when unknown (event still emitted, undated). */\n date: string | null;\n}\n\nexport interface AssembleJourneyInput {\n belief: JourneyBeliefInput;\n /** The belief's own readings (already filtered to this assumption). */\n readings: AttributionReadingInput[];\n /** The experiments testing this belief. */\n experiments: JourneyExperimentInput[];\n /** \"Now\" as an ISO date — passed in so the log stays pure. */\n now: string;\n}\n\n/**\n * Assemble one belief's chronological event log. Events sort by date, undated\n * events anchored to the bet's date and ordered structurally; `now` is always\n * last.\n */\nexport function assembleJourney(input: AssembleJourneyInput): JourneyEvent[] {\n const { belief, readings, experiments, now } = input;\n const events: JourneyEvent[] = [];\n\n if (belief.createdAt) {\n events.push(event(\"bet\", belief.createdAt));\n }\n if (belief.impactScored) {\n // No scored-on date is stored, so the score rides structurally after the\n // bet rather than carrying an invented date.\n events.push(event(\"score\", null));\n }\n for (const e of experiments) {\n events.push(event(\"experiment\", e.date, { refId: e.id }));\n }\n\n const trajectory = confidenceTrajectory(readings);\n const confByDate = new Map(trajectory.map((p) => [p.date, p.confidence]));\n for (const r of readings) {\n const dated = r.date ?? null;\n // Confidence is known only for a concluded reading that lands on a\n // trajectory date (undated / inconclusive readings carry no number).\n const conf =\n dated && isConcluded(r.result) ? (confByDate.get(dated) ?? null) : null;\n events.push(\n event(\"reading\", dated, { confidence: conf, result: r.result, refId: r.id }),\n );\n }\n\n // The first point at which the evidence dragged Confidence into the kill zone.\n const cross = trajectory.find((p) => p.confidence <= KILL_LANE_THRESHOLD);\n if (cross) {\n events.push(event(\"confidence-cross\", cross.date, { confidence: cross.confidence }));\n }\n\n // Undated events anchor to the bet so they sit at the start in structural\n // order; `now` is appended after sorting so it is unconditionally last.\n const anchor = belief.createdAt ?? \"\";\n events.sort((a, b) => {\n const ka = a.date ?? anchor;\n const kb = b.date ?? anchor;\n if (ka !== kb) return ka < kb ? -1 : 1;\n return KIND_ORDER[a.kind] - KIND_ORDER[b.kind];\n });\n events.push(event(\"now\", now, { confidence: confidence(readings) }));\n\n return events;\n}\n\nfunction event(\n kind: JourneyEventKind,\n date: string | null,\n extra: Partial<Pick<JourneyEvent, \"confidence\" | \"result\" | \"refId\">> = {},\n): JourneyEvent {\n return {\n kind,\n date,\n confidence: extra.confidence ?? null,\n result: extra.result ?? null,\n refId: extra.refId ?? null,\n };\n}\n"],"mappings":"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","names":["risk","band","band","confidence","confidence"]}
|