@lucern/confidence 1.0.28 → 1.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +7 -7
- package/dist/index.js +905 -770
- package/dist/index.js.map +1 -1
- package/dist/proof-attestation.json +1 -1
- package/dist/v1/codec.js.map +1 -1
- package/dist/v1/index.d.ts +7 -7
- package/dist/v1/index.js +905 -770
- package/dist/v1/index.js.map +1 -1
- package/dist/v1/interfaces.d.ts +5 -5
- package/dist/v1/operations/approximation.d.ts +1 -1
- package/dist/v1/operations/approximation.js +8 -7
- package/dist/v1/operations/approximation.js.map +1 -1
- package/dist/v1/operations/bridge/index.js +7 -6
- package/dist/v1/operations/bridge/index.js.map +1 -1
- package/dist/v1/operations/canonical.d.ts +1 -1
- package/dist/v1/operations/canonical.js +106 -57
- package/dist/v1/operations/canonical.js.map +1 -1
- package/dist/v1/operations/contracts/epistemicContract.d.ts +1 -1
- package/dist/v1/operations/contracts/epistemicContract.js +9 -3
- package/dist/v1/operations/contracts/epistemicContract.js.map +1 -1
- package/dist/v1/operations/contradiction/detectTupleContradiction.js.map +1 -1
- package/dist/v1/operations/contradiction/index.js.map +1 -1
- package/dist/v1/operations/dynamics/cascade.js +1 -1
- package/dist/v1/operations/dynamics/cascade.js.map +1 -1
- package/dist/v1/operations/dynamics/decay.js +105 -41
- package/dist/v1/operations/dynamics/decay.js.map +1 -1
- package/dist/v1/operations/dynamics/defeat.js.map +1 -1
- package/dist/v1/operations/dynamics/propagation.d.ts +3 -3
- package/dist/v1/operations/dynamics/propagation.js +101 -57
- package/dist/v1/operations/dynamics/propagation.js.map +1 -1
- package/dist/v1/operations/dynamics/revision.js +7 -6
- package/dist/v1/operations/dynamics/revision.js.map +1 -1
- package/dist/v1/operations/index.js +8 -3
- package/dist/v1/operations/index.js.map +1 -1
- package/dist/v1/operations/lucern.d.ts +7 -7
- package/dist/v1/operations/lucern.js +934 -807
- package/dist/v1/operations/lucern.js.map +1 -1
- package/dist/v1/operations/operatorTaxonomy.d.ts +10 -10
- package/dist/v1/operations/operatorTaxonomy.js.map +1 -1
- package/dist/v1/operations/scoring.d.ts +2 -1
- package/dist/v1/operations/scoring.js +20 -5
- package/dist/v1/operations/scoring.js.map +1 -1
- package/dist/v1/operations/subjectiveLogic/index.js +91 -54
- package/dist/v1/operations/subjectiveLogic/index.js.map +1 -1
- package/dist/v1/operations/temporalDecay.d.ts +3 -3
- package/dist/v1/operations/temporalDecay.js +8 -3
- package/dist/v1/operations/temporalDecay.js.map +1 -1
- package/dist/v1/types.d.ts +101 -101
- package/package.json +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
// src/v1/codec.ts
|
|
4
|
+
|
|
3
5
|
// src/v1/operations/subjectiveLogic/index.ts
|
|
4
6
|
function opinion(belief, disbelief, uncertainty, baseRate) {
|
|
5
7
|
const b = Number.isFinite(belief) ? Math.max(0, belief) : 0;
|
|
@@ -107,14 +109,91 @@ function computeConditionalDeductionBaseRate(opinionA, ifTrue, ifFalse, fallback
|
|
|
107
109
|
}
|
|
108
110
|
function safeCorrectionTerm(numerator, denominator) {
|
|
109
111
|
if (Math.abs(denominator) <= EPSILON) {
|
|
110
|
-
return
|
|
112
|
+
return;
|
|
111
113
|
}
|
|
112
114
|
const value = numerator / denominator;
|
|
113
115
|
if (!Number.isFinite(value)) {
|
|
114
|
-
return
|
|
116
|
+
return;
|
|
115
117
|
}
|
|
116
118
|
return Math.max(0, value);
|
|
117
119
|
}
|
|
120
|
+
function correctionOrZero(numerator, denominator) {
|
|
121
|
+
return safeCorrectionTerm(numerator, denominator) ?? 0;
|
|
122
|
+
}
|
|
123
|
+
function hasNoConditionalDeductionCorrection(ifTrue, ifFalse) {
|
|
124
|
+
return ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d || ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d;
|
|
125
|
+
}
|
|
126
|
+
function usesLowerVacuousBranch(context) {
|
|
127
|
+
return context.projectedVacuousDeduction <= context.projectedConditionalA;
|
|
128
|
+
}
|
|
129
|
+
function usesLowerAntecedentBranch(context) {
|
|
130
|
+
return context.projectedAntecedent <= context.opinionA.a;
|
|
131
|
+
}
|
|
132
|
+
function computeTrueDominantDeductionCorrection(context) {
|
|
133
|
+
const beliefGap = context.ifTrue.b - context.ifFalse.b;
|
|
134
|
+
const disbeliefGap = context.ifFalse.d - context.ifTrue.d;
|
|
135
|
+
const lowerVacuous = usesLowerVacuousBranch(context);
|
|
136
|
+
const lowerAntecedent = usesLowerAntecedentBranch(context);
|
|
137
|
+
if (lowerVacuous && lowerAntecedent) {
|
|
138
|
+
return correctionOrZero(
|
|
139
|
+
context.opinionA.a * context.opinionA.u * (context.intermediateBelief - context.ifTrue.b),
|
|
140
|
+
context.projectedAntecedent * context.childBaseRate
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
if (lowerVacuous) {
|
|
144
|
+
return correctionOrZero(
|
|
145
|
+
context.opinionA.a * context.opinionA.u * (context.intermediateDisbelief - context.ifTrue.d) * beliefGap,
|
|
146
|
+
context.projectedAntecedentComplement * context.childBaseRate * disbeliefGap
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
if (lowerAntecedent) {
|
|
150
|
+
return correctionOrZero(
|
|
151
|
+
(1 - context.opinionA.a) * context.opinionA.u * (context.intermediateBelief - context.ifTrue.b) * disbeliefGap,
|
|
152
|
+
context.projectedAntecedent * (1 - context.childBaseRate) * beliefGap
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return correctionOrZero(
|
|
156
|
+
(1 - context.opinionA.a) * context.opinionA.u * (context.intermediateDisbelief - context.ifTrue.d),
|
|
157
|
+
context.projectedAntecedentComplement * (1 - context.childBaseRate)
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
function computeFalseDominantDeductionCorrection(context) {
|
|
161
|
+
const beliefGap = context.ifFalse.b - context.ifTrue.b;
|
|
162
|
+
const disbeliefGap = context.ifTrue.d - context.ifFalse.d;
|
|
163
|
+
const lowerVacuous = usesLowerVacuousBranch(context);
|
|
164
|
+
const lowerAntecedent = usesLowerAntecedentBranch(context);
|
|
165
|
+
if (lowerVacuous && lowerAntecedent) {
|
|
166
|
+
return correctionOrZero(
|
|
167
|
+
(1 - context.opinionA.a) * context.opinionA.u * (context.intermediateDisbelief - context.ifTrue.d) * beliefGap,
|
|
168
|
+
context.projectedAntecedent * context.childBaseRate * disbeliefGap
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
if (lowerVacuous) {
|
|
172
|
+
return correctionOrZero(
|
|
173
|
+
(1 - context.opinionA.a) * context.opinionA.u * (context.intermediateBelief - context.ifTrue.b),
|
|
174
|
+
context.projectedAntecedentComplement * context.childBaseRate
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
if (lowerAntecedent) {
|
|
178
|
+
return correctionOrZero(
|
|
179
|
+
context.opinionA.a * context.opinionA.u * (context.intermediateDisbelief - context.ifTrue.d),
|
|
180
|
+
context.projectedAntecedent * (1 - context.childBaseRate)
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
return correctionOrZero(
|
|
184
|
+
context.opinionA.a * context.opinionA.u * (context.intermediateBelief - context.ifTrue.b) * disbeliefGap,
|
|
185
|
+
context.projectedAntecedentComplement * (1 - context.childBaseRate) * beliefGap
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
function computeConditionalDeductionCorrection(context) {
|
|
189
|
+
if (hasNoConditionalDeductionCorrection(context.ifTrue, context.ifFalse)) {
|
|
190
|
+
return 0;
|
|
191
|
+
}
|
|
192
|
+
if (context.ifTrue.b > context.ifFalse.b && context.ifTrue.d <= context.ifFalse.d) {
|
|
193
|
+
return computeTrueDominantDeductionCorrection(context);
|
|
194
|
+
}
|
|
195
|
+
return computeFalseDominantDeductionCorrection(context);
|
|
196
|
+
}
|
|
118
197
|
function conditionalDeduction(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
|
|
119
198
|
const fallbackChildBaseRate = childBaseRateFallback(
|
|
120
199
|
ifTrue,
|
|
@@ -134,58 +213,18 @@ function conditionalDeduction(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
|
|
|
134
213
|
const intermediateUncertainty = opinionA.b * ifTrue.u + opinionA.d * ifFalse.u + opinionA.u * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
|
|
135
214
|
const projectedVacuousDeduction = ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a) + childBaseRate * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
|
|
136
215
|
const projectedConditionalA = ifTrue.b + childBaseRate * (1 - ifTrue.b - ifTrue.d);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
correction = safeCorrectionTerm(
|
|
150
|
-
opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
|
|
151
|
-
projectedAntecedentComplement * childBaseRate * disbeliefGap
|
|
152
|
-
) ?? 0;
|
|
153
|
-
} else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
154
|
-
correction = safeCorrectionTerm(
|
|
155
|
-
(1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
|
|
156
|
-
projectedAntecedent * (1 - childBaseRate) * beliefGap
|
|
157
|
-
) ?? 0;
|
|
158
|
-
} else {
|
|
159
|
-
correction = safeCorrectionTerm(
|
|
160
|
-
(1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d),
|
|
161
|
-
projectedAntecedentComplement * (1 - childBaseRate)
|
|
162
|
-
) ?? 0;
|
|
163
|
-
}
|
|
164
|
-
} else {
|
|
165
|
-
const beliefGap = ifFalse.b - ifTrue.b;
|
|
166
|
-
const disbeliefGap = ifTrue.d - ifFalse.d;
|
|
167
|
-
if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
168
|
-
correction = safeCorrectionTerm(
|
|
169
|
-
(1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
|
|
170
|
-
projectedAntecedent * childBaseRate * disbeliefGap
|
|
171
|
-
) ?? 0;
|
|
172
|
-
} else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
|
|
173
|
-
correction = safeCorrectionTerm(
|
|
174
|
-
(1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b),
|
|
175
|
-
projectedAntecedentComplement * childBaseRate
|
|
176
|
-
) ?? 0;
|
|
177
|
-
} else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
178
|
-
correction = safeCorrectionTerm(
|
|
179
|
-
opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d),
|
|
180
|
-
projectedAntecedent * (1 - childBaseRate)
|
|
181
|
-
) ?? 0;
|
|
182
|
-
} else {
|
|
183
|
-
correction = safeCorrectionTerm(
|
|
184
|
-
opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
|
|
185
|
-
projectedAntecedentComplement * (1 - childBaseRate) * beliefGap
|
|
186
|
-
) ?? 0;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
216
|
+
const correction = computeConditionalDeductionCorrection({
|
|
217
|
+
childBaseRate,
|
|
218
|
+
ifFalse,
|
|
219
|
+
ifTrue,
|
|
220
|
+
intermediateBelief,
|
|
221
|
+
intermediateDisbelief,
|
|
222
|
+
opinionA,
|
|
223
|
+
projectedAntecedent,
|
|
224
|
+
projectedAntecedentComplement,
|
|
225
|
+
projectedConditionalA,
|
|
226
|
+
projectedVacuousDeduction
|
|
227
|
+
});
|
|
189
228
|
return opinion(
|
|
190
229
|
intermediateBelief - childBaseRate * correction,
|
|
191
230
|
intermediateDisbelief - (1 - childBaseRate) * correction,
|
|
@@ -257,107 +296,7 @@ function informationGain(o) {
|
|
|
257
296
|
return o.u * (1 - Math.abs(o.b - o.d));
|
|
258
297
|
}
|
|
259
298
|
|
|
260
|
-
// src/v1/
|
|
261
|
-
function finiteNumber(value) {
|
|
262
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
263
|
-
}
|
|
264
|
-
function requiredOpinionNumber(label, ...values) {
|
|
265
|
-
for (const value of values) {
|
|
266
|
-
const numberValue = finiteNumber(value);
|
|
267
|
-
if (numberValue !== void 0) {
|
|
268
|
-
return numberValue;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
throw new Error(`Opinion record is missing required ${label}.`);
|
|
272
|
-
}
|
|
273
|
-
function clamp01(value) {
|
|
274
|
-
return Math.max(0, Math.min(1, value));
|
|
275
|
-
}
|
|
276
|
-
function confidenceFromOpinion(opinion2) {
|
|
277
|
-
return clamp01(opinion2.b + opinion2.a * opinion2.u);
|
|
278
|
-
}
|
|
279
|
-
function toStoredOpinionFields(opinion2) {
|
|
280
|
-
return {
|
|
281
|
-
belief: opinion2.b,
|
|
282
|
-
disbelief: opinion2.d,
|
|
283
|
-
uncertainty: opinion2.u,
|
|
284
|
-
baseRate: opinion2.a
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
function readOpinionFromRecord(source) {
|
|
288
|
-
const record = source && typeof source === "object" ? source : {};
|
|
289
|
-
return mkOpinion(
|
|
290
|
-
requiredOpinionNumber(
|
|
291
|
-
"belief",
|
|
292
|
-
record.b,
|
|
293
|
-
record.belief,
|
|
294
|
-
record.slBelief,
|
|
295
|
-
record.opinion_b
|
|
296
|
-
),
|
|
297
|
-
requiredOpinionNumber(
|
|
298
|
-
"disbelief",
|
|
299
|
-
record.d,
|
|
300
|
-
record.disbelief,
|
|
301
|
-
record.slDisbelief,
|
|
302
|
-
record.opinion_d
|
|
303
|
-
),
|
|
304
|
-
requiredOpinionNumber(
|
|
305
|
-
"uncertainty",
|
|
306
|
-
record.u,
|
|
307
|
-
record.uncertainty,
|
|
308
|
-
record.slUncertainty,
|
|
309
|
-
record.opinion_u
|
|
310
|
-
),
|
|
311
|
-
requiredOpinionNumber(
|
|
312
|
-
"baseRate",
|
|
313
|
-
record.a,
|
|
314
|
-
record.baseRate,
|
|
315
|
-
record.slBaseRate,
|
|
316
|
-
record.opinion_a
|
|
317
|
-
)
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
function opinionFromScalar(value, mode, options) {
|
|
321
|
-
const clampedValue = clamp01(value);
|
|
322
|
-
const baseRate = options?.baseRate === void 0 ? void 0 : clamp01(options.baseRate);
|
|
323
|
-
switch (mode) {
|
|
324
|
-
case "base_rate":
|
|
325
|
-
return mkOpinion(0, 0, 1, clampedValue);
|
|
326
|
-
case "dogmatic":
|
|
327
|
-
if (baseRate === void 0) {
|
|
328
|
-
throw new Error('opinionFromScalar(value, "dogmatic") requires options.baseRate.');
|
|
329
|
-
}
|
|
330
|
-
return mkOpinion(clampedValue, 1 - clampedValue, 0, baseRate);
|
|
331
|
-
case "projected_with_u": {
|
|
332
|
-
if (baseRate === void 0) {
|
|
333
|
-
throw new Error(
|
|
334
|
-
'opinionFromScalar(value, "projected_with_u") requires options.baseRate.'
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
const uncertainty = options?.uncertainty;
|
|
338
|
-
if (uncertainty === void 0) {
|
|
339
|
-
throw new Error(
|
|
340
|
-
'opinionFromScalar(value, "projected_with_u") requires options.uncertainty.'
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
const clampedUncertainty = clamp01(uncertainty);
|
|
344
|
-
const evidenceWeight = 1 - clampedUncertainty;
|
|
345
|
-
return mkOpinion(
|
|
346
|
-
clampedValue * evidenceWeight,
|
|
347
|
-
(1 - clampedValue) * evidenceWeight,
|
|
348
|
-
clampedUncertainty,
|
|
349
|
-
baseRate
|
|
350
|
-
);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
throw new Error(`Unsupported opinionFromScalar mode: ${mode}`);
|
|
354
|
-
}
|
|
355
|
-
function toDogmaticOpinion(confidence, baseRate) {
|
|
356
|
-
return opinionFromScalar(confidence, "dogmatic", { baseRate });
|
|
357
|
-
}
|
|
358
|
-
function hasProjectedOpinionChanged(current, next, tolerance = 0.01) {
|
|
359
|
-
return Math.abs(confidenceFromOpinion(next) - confidenceFromOpinion(current)) >= tolerance;
|
|
360
|
-
}
|
|
299
|
+
// src/v1/codec.ts
|
|
361
300
|
var SL_EPSILON = 1e-9;
|
|
362
301
|
z.object({
|
|
363
302
|
b: z.number(),
|
|
@@ -393,395 +332,460 @@ function toStorage(opinion2) {
|
|
|
393
332
|
baseRate: opinion2.a
|
|
394
333
|
};
|
|
395
334
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
335
|
+
|
|
336
|
+
// src/v1/operations/contracts/epistemicContract.ts
|
|
337
|
+
var BUILT_IN_METRIC_CHECKER = "metric_checker";
|
|
338
|
+
var BUILT_IN_REFERENCE_CHECK_COUNTER = "reference_check_counter";
|
|
339
|
+
var BUILT_IN_MARKET_INDEX_COMPARATOR = "market_index_comparator";
|
|
340
|
+
function clampConfidence(value) {
|
|
341
|
+
return Math.max(0, Math.min(1, value));
|
|
342
|
+
}
|
|
343
|
+
function generateContractId() {
|
|
344
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
345
|
+
return crypto.randomUUID();
|
|
399
346
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
347
|
+
return `contract-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
348
|
+
}
|
|
349
|
+
function isRecord(value) {
|
|
350
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
351
|
+
}
|
|
352
|
+
function deriveContractStatus(result, currentStatus) {
|
|
353
|
+
if (currentStatus === "archived") {
|
|
354
|
+
return currentStatus;
|
|
355
|
+
}
|
|
356
|
+
switch (result) {
|
|
357
|
+
case "confirmed":
|
|
358
|
+
return "satisfied";
|
|
359
|
+
case "disconfirmed":
|
|
360
|
+
return "violated";
|
|
361
|
+
case "expired":
|
|
362
|
+
return "expired";
|
|
363
|
+
default:
|
|
364
|
+
if (currentStatus === "satisfied" || currentStatus === "violated" || currentStatus === "expired") {
|
|
365
|
+
return "active";
|
|
366
|
+
}
|
|
367
|
+
return currentStatus;
|
|
403
368
|
}
|
|
404
|
-
return void 0;
|
|
405
369
|
}
|
|
406
|
-
function
|
|
407
|
-
|
|
408
|
-
|
|
370
|
+
function deriveVerificationTrigger(result) {
|
|
371
|
+
switch (result) {
|
|
372
|
+
case "confirmed":
|
|
373
|
+
return "verification_confirmed";
|
|
374
|
+
case "disconfirmed":
|
|
375
|
+
return "verification_disconfirmed";
|
|
376
|
+
case "expired":
|
|
377
|
+
return "verification_expired";
|
|
378
|
+
case "partial":
|
|
379
|
+
return "verification_partial";
|
|
380
|
+
default:
|
|
381
|
+
return null;
|
|
409
382
|
}
|
|
410
|
-
return Math.max(0, halfLifeMs);
|
|
411
383
|
}
|
|
412
|
-
function
|
|
413
|
-
const
|
|
414
|
-
if (
|
|
415
|
-
return
|
|
384
|
+
function deriveContractModulationPlan(args) {
|
|
385
|
+
const trigger = deriveVerificationTrigger(args.result);
|
|
386
|
+
if (!trigger) {
|
|
387
|
+
return null;
|
|
416
388
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
389
|
+
if (args.result === "confirmed") {
|
|
390
|
+
const rawNext = args.currentConfidence + args.modulation.onConfirmed.delta;
|
|
391
|
+
const confidenceAfter = clampConfidence(
|
|
392
|
+
Math.min(
|
|
393
|
+
args.modulation.onConfirmed.ceiling ?? Number.POSITIVE_INFINITY,
|
|
394
|
+
rawNext
|
|
395
|
+
)
|
|
396
|
+
);
|
|
397
|
+
return {
|
|
398
|
+
trigger,
|
|
399
|
+
confidenceBefore: args.currentConfidence,
|
|
400
|
+
confidenceAfter,
|
|
401
|
+
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
402
|
+
};
|
|
421
403
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
404
|
+
if (args.result === "disconfirmed") {
|
|
405
|
+
const rawNext = args.currentConfidence + args.modulation.onDisconfirmed.delta;
|
|
406
|
+
const confidenceAfter = clampConfidence(
|
|
407
|
+
Math.max(args.modulation.onDisconfirmed.floor ?? 0, rawNext)
|
|
408
|
+
);
|
|
409
|
+
return {
|
|
410
|
+
trigger,
|
|
411
|
+
confidenceBefore: args.currentConfidence,
|
|
412
|
+
confidenceAfter,
|
|
413
|
+
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
414
|
+
};
|
|
425
415
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
416
|
+
if (args.result === "expired" && args.modulation.onExpired) {
|
|
417
|
+
const confidenceAfter = clampConfidence(
|
|
418
|
+
args.currentConfidence + args.modulation.onExpired.delta
|
|
419
|
+
);
|
|
420
|
+
return {
|
|
421
|
+
trigger,
|
|
422
|
+
confidenceBefore: args.currentConfidence,
|
|
423
|
+
confidenceAfter,
|
|
424
|
+
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
if (args.result === "partial" && args.modulation.onPartial) {
|
|
428
|
+
if ((args.resultConfidence ?? 0) < args.modulation.onPartial.threshold) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
const confidenceAfter = clampConfidence(
|
|
432
|
+
args.currentConfidence + args.modulation.onPartial.delta
|
|
433
|
+
);
|
|
434
|
+
return {
|
|
435
|
+
trigger,
|
|
436
|
+
confidenceBefore: args.currentConfidence,
|
|
437
|
+
confidenceAfter,
|
|
438
|
+
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
return null;
|
|
431
442
|
}
|
|
432
|
-
|
|
433
|
-
// src/v1/operations/contradiction/detectTupleContradiction.ts
|
|
434
|
-
var DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD = 0.7;
|
|
435
|
-
var DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD = 0.7;
|
|
436
|
-
function normalizeTupleContradictionPolicy(policy = {}) {
|
|
443
|
+
function createInheritedContractRecord(contract, args) {
|
|
437
444
|
return {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
445
|
+
beliefNodeId: args.beliefNodeId,
|
|
446
|
+
contractId: generateContractId(),
|
|
447
|
+
title: contract.title,
|
|
448
|
+
description: contract.description,
|
|
449
|
+
conditionType: contract.conditionType,
|
|
450
|
+
direction: contract.direction,
|
|
451
|
+
condition: contract.condition,
|
|
452
|
+
deadline: contract.deadline,
|
|
453
|
+
compositeOf: contract.compositeOf,
|
|
454
|
+
compositeOperator: contract.compositeOperator,
|
|
455
|
+
modulation: contract.modulation,
|
|
456
|
+
evaluationSchedule: contract.evaluationSchedule,
|
|
457
|
+
periodicIntervalMs: contract.periodicIntervalMs,
|
|
458
|
+
status: "active",
|
|
459
|
+
lineageSource: "inherited",
|
|
460
|
+
inheritedFromContractId: contract.contractId,
|
|
461
|
+
inheritedFromBeliefNodeId: contract.beliefNodeId,
|
|
462
|
+
inheritedAt: args.now,
|
|
463
|
+
topicId: args.topicId,
|
|
464
|
+
createdAt: args.now,
|
|
465
|
+
createdBy: args.createdBy,
|
|
466
|
+
updatedAt: args.now
|
|
444
467
|
};
|
|
445
468
|
}
|
|
446
|
-
function
|
|
447
|
-
return
|
|
469
|
+
function normalizeEvidentialAction(value) {
|
|
470
|
+
return value === "append_sl_scoring" || value === "flag_review" || value === "archive" ? value : void 0;
|
|
448
471
|
}
|
|
449
|
-
function
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
472
|
+
function parseComparisonOperator(value, evaluatorName) {
|
|
473
|
+
if (value === "gte" || value === "lte" || value === "eq" || value === "gt" || value === "lt") {
|
|
474
|
+
return value;
|
|
475
|
+
}
|
|
476
|
+
throw new Error(
|
|
477
|
+
`${evaluatorName} requires operator to be one of gte, lte, eq, gt, or lt.`
|
|
455
478
|
);
|
|
456
|
-
|
|
479
|
+
}
|
|
480
|
+
function parseNumericThreshold(value, evaluatorName) {
|
|
481
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
482
|
+
return value;
|
|
483
|
+
}
|
|
484
|
+
throw new Error(`${evaluatorName} requires a finite numeric threshold.`);
|
|
485
|
+
}
|
|
486
|
+
function pickFiniteNumber(config, keys) {
|
|
487
|
+
for (const key of keys) {
|
|
488
|
+
const value = config[key];
|
|
489
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
490
|
+
return value;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
function getEvaluatorInputRecord(inputData, nestedKey) {
|
|
496
|
+
if (!isRecord(inputData)) {
|
|
497
|
+
return {};
|
|
498
|
+
}
|
|
499
|
+
const nested = inputData[nestedKey];
|
|
500
|
+
if (isRecord(nested)) {
|
|
501
|
+
return nested;
|
|
502
|
+
}
|
|
503
|
+
return inputData;
|
|
504
|
+
}
|
|
505
|
+
function parseEvidentialEvaluatorConfig(value) {
|
|
506
|
+
if (!isRecord(value)) {
|
|
507
|
+
throw new Error(
|
|
508
|
+
"Evidential contracts require condition.evaluatorConfig with metric/operator/threshold."
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
const config = value;
|
|
512
|
+
const metric = config.metric;
|
|
513
|
+
const operator = config.operator;
|
|
514
|
+
const threshold = config.threshold;
|
|
515
|
+
if (metric !== "evidence_count" && metric !== "contradiction_status" && metric !== "edge_freshness" && metric !== "dependent_count") {
|
|
516
|
+
throw new Error(`Unsupported evidential metric: ${String(metric)}`);
|
|
517
|
+
}
|
|
518
|
+
if (operator !== "gte" && operator !== "lte" && operator !== "eq" && operator !== "gt" && operator !== "lt") {
|
|
519
|
+
throw new Error(`Unsupported evidential operator: ${String(operator)}`);
|
|
520
|
+
}
|
|
521
|
+
if (typeof threshold !== "number" || !Number.isFinite(threshold)) {
|
|
522
|
+
throw new Error("Evidential contracts require a numeric threshold.");
|
|
523
|
+
}
|
|
524
|
+
const actionParams = isRecord(config.actionParams) ? config.actionParams : void 0;
|
|
457
525
|
return {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
526
|
+
metric,
|
|
527
|
+
operator,
|
|
528
|
+
threshold,
|
|
529
|
+
action: normalizeEvidentialAction(config.action),
|
|
530
|
+
actionParams: actionParams && (typeof actionParams.targetConfidence === "number" || typeof actionParams.rationale === "string") ? {
|
|
531
|
+
targetConfidence: typeof actionParams.targetConfidence === "number" ? actionParams.targetConfidence : void 0,
|
|
532
|
+
rationale: typeof actionParams.rationale === "string" ? actionParams.rationale : void 0
|
|
533
|
+
} : void 0
|
|
462
534
|
};
|
|
463
535
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
return
|
|
478
|
-
0,
|
|
479
|
-
beliefOpinion.d + beliefOpinion.b * 0.5,
|
|
480
|
-
0.5,
|
|
481
|
-
beliefOpinion.a
|
|
482
|
-
);
|
|
483
|
-
}
|
|
484
|
-
return beliefOpinion;
|
|
536
|
+
function compareMetricValue(operator, left, right) {
|
|
537
|
+
switch (operator) {
|
|
538
|
+
case "gte":
|
|
539
|
+
return left >= right;
|
|
540
|
+
case "lte":
|
|
541
|
+
return left <= right;
|
|
542
|
+
case "eq":
|
|
543
|
+
return left === right;
|
|
544
|
+
case "gt":
|
|
545
|
+
return left > right;
|
|
546
|
+
case "lt":
|
|
547
|
+
return left < right;
|
|
548
|
+
default:
|
|
549
|
+
return false;
|
|
485
550
|
}
|
|
486
|
-
const dampingFactor = Math.pow(dependencyProjection, 0.5);
|
|
487
|
-
return opinion(
|
|
488
|
-
beliefOpinion.b * dampingFactor,
|
|
489
|
-
beliefOpinion.d + beliefOpinion.b * (1 - dampingFactor) * 0.3,
|
|
490
|
-
beliefOpinion.u + beliefOpinion.b * (1 - dampingFactor) * 0.7,
|
|
491
|
-
beliefOpinion.a
|
|
492
|
-
);
|
|
493
551
|
}
|
|
494
|
-
function
|
|
552
|
+
function resolveComparisonResult(direction, comparisonSatisfied) {
|
|
553
|
+
if (direction === "falsifies") {
|
|
554
|
+
return comparisonSatisfied ? "disconfirmed" : "confirmed";
|
|
555
|
+
}
|
|
556
|
+
return comparisonSatisfied ? "confirmed" : "disconfirmed";
|
|
557
|
+
}
|
|
558
|
+
function buildComparisonRationale(args) {
|
|
559
|
+
const renderedObserved = args.observedValue === null ? "no data" : `${args.observedValue}${args.unit ? ` ${args.unit}` : ""}`;
|
|
560
|
+
const renderedThreshold = `${args.threshold}${args.unit ? ` ${args.unit}` : ""}`;
|
|
561
|
+
const clause = `${args.label} observed ${renderedObserved} against ${args.operator} ${renderedThreshold}`;
|
|
562
|
+
if (args.observedValue === null) {
|
|
563
|
+
return `${clause}; evaluator returned ${args.result} because no current data was available.`;
|
|
564
|
+
}
|
|
565
|
+
return `${clause}; comparison ${args.comparisonSatisfied ? "passed" : "failed"}, resulting in ${args.result}.`;
|
|
566
|
+
}
|
|
567
|
+
function buildEvidentialRationale(args) {
|
|
568
|
+
const observed = args.snapshot.value === null ? "no data" : String(args.snapshot.value);
|
|
569
|
+
const clause = `${args.snapshot.metric} observed ${observed} against ${args.config.operator} ${args.config.threshold}`;
|
|
570
|
+
if (args.snapshot.value === null) {
|
|
571
|
+
return `${clause}; evidential evaluator treated the comparison as unmet, resulting in ${args.result}.`;
|
|
572
|
+
}
|
|
573
|
+
return `${clause}; comparison ${args.comparisonSatisfied ? "passed" : "failed"}, resulting in ${args.result}.`;
|
|
574
|
+
}
|
|
575
|
+
function parseMetricCheckerConfig(value) {
|
|
576
|
+
if (!isRecord(value)) {
|
|
577
|
+
throw new Error(
|
|
578
|
+
"metric_checker requires condition.evaluatorConfig with observedValue/operator/threshold."
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
const config = value;
|
|
495
582
|
return {
|
|
496
|
-
|
|
497
|
-
operator:
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
dependencyOpinion.a
|
|
504
|
-
)
|
|
505
|
-
).toFixed(2)}`
|
|
583
|
+
metric: typeof config.metric === "string" && config.metric.length > 0 ? config.metric : void 0,
|
|
584
|
+
operator: parseComparisonOperator(config.operator, BUILT_IN_METRIC_CHECKER),
|
|
585
|
+
threshold: parseNumericThreshold(config.threshold, BUILT_IN_METRIC_CHECKER),
|
|
586
|
+
observedValue: pickFiniteNumber(config, ["observedValue", "value"]),
|
|
587
|
+
currentValue: pickFiniteNumber(config, ["currentValue"]),
|
|
588
|
+
metricValue: pickFiniteNumber(config, ["metricValue"]),
|
|
589
|
+
unit: typeof config.unit === "string" && config.unit.length > 0 ? config.unit : void 0
|
|
506
590
|
};
|
|
507
591
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
const targetOpinion = mkOpinion(target.b, target.d, target.u, target.a);
|
|
513
|
-
if (metadata.constraint === "xor") {
|
|
514
|
-
const result = constraintFusion(
|
|
515
|
-
sourceOpinion,
|
|
516
|
-
targetOpinion,
|
|
517
|
-
metadata.normalization ?? "pressure"
|
|
592
|
+
function parseReferenceCheckCounterConfig(value) {
|
|
593
|
+
if (!isRecord(value)) {
|
|
594
|
+
throw new Error(
|
|
595
|
+
"reference_check_counter requires condition.evaluatorConfig with tag/operator/threshold."
|
|
518
596
|
);
|
|
519
|
-
return {
|
|
520
|
-
opinion: result.o2,
|
|
521
|
-
operator: "constraint_fusion",
|
|
522
|
-
rationale: `XOR constraint: source belief at ${project(
|
|
523
|
-
sourceOpinion
|
|
524
|
-
).toFixed(2)} pressures target`
|
|
525
|
-
};
|
|
526
597
|
}
|
|
527
|
-
const
|
|
598
|
+
const config = value;
|
|
599
|
+
if (typeof config.tag !== "string" || config.tag.trim().length === 0) {
|
|
600
|
+
throw new Error("reference_check_counter requires a non-empty tag.");
|
|
601
|
+
}
|
|
528
602
|
return {
|
|
529
|
-
|
|
530
|
-
operator:
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
)
|
|
603
|
+
tag: config.tag.trim(),
|
|
604
|
+
operator: parseComparisonOperator(
|
|
605
|
+
config.operator,
|
|
606
|
+
BUILT_IN_REFERENCE_CHECK_COUNTER
|
|
607
|
+
),
|
|
608
|
+
threshold: parseNumericThreshold(
|
|
609
|
+
config.threshold,
|
|
610
|
+
BUILT_IN_REFERENCE_CHECK_COUNTER
|
|
611
|
+
),
|
|
612
|
+
caseSensitive: config.caseSensitive === true
|
|
534
613
|
};
|
|
535
614
|
}
|
|
536
|
-
function
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
615
|
+
function parseTemporalDeadlineConfig(value) {
|
|
616
|
+
if (!isRecord(value)) {
|
|
617
|
+
return {};
|
|
618
|
+
}
|
|
619
|
+
const config = value;
|
|
540
620
|
return {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
621
|
+
label: typeof config.label === "string" && config.label.length > 0 ? config.label : void 0,
|
|
622
|
+
completed: config.completed === true,
|
|
623
|
+
completedAt: pickFiniteNumber(config, ["completedAt"]),
|
|
624
|
+
observedAt: pickFiniteNumber(config, ["observedAt"]),
|
|
625
|
+
satisfiedAt: pickFiniteNumber(config, ["satisfiedAt"]),
|
|
626
|
+
achievedAt: pickFiniteNumber(config, ["achievedAt"])
|
|
544
627
|
};
|
|
545
628
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
handler: (source, target, weight, metadata) => {
|
|
552
|
-
const sourceOpinion = mkOpinion(source.b, source.d, source.u, source.a);
|
|
553
|
-
const targetOpinion = mkOpinion(target.b, target.d, target.u, target.a);
|
|
554
|
-
if (weight < 0) {
|
|
555
|
-
return applyNegativeSupport(sourceOpinion, targetOpinion, weight, metadata);
|
|
556
|
-
}
|
|
557
|
-
const discounted = trustDiscount(sourceOpinion, weight);
|
|
558
|
-
return {
|
|
559
|
-
opinion: cumulativeFusion(targetOpinion, discounted),
|
|
560
|
-
operator: "cumulative_fusion",
|
|
561
|
-
rationale: `Supporting evidence (weight=${weight.toFixed(
|
|
562
|
-
2
|
|
563
|
-
)}) from source at ${project(sourceOpinion).toFixed(2)}`
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
},
|
|
567
|
-
informs: {
|
|
568
|
-
direction: "outgoing",
|
|
569
|
-
handler: (source, target, weight) => {
|
|
570
|
-
const sourceOpinion = mkOpinion(source.b, source.d, source.u, source.a);
|
|
571
|
-
const targetOpinion = mkOpinion(target.b, target.d, target.u, target.a);
|
|
572
|
-
if (weight < 0) {
|
|
573
|
-
return applyNegativeEvidence(sourceOpinion, targetOpinion, weight);
|
|
574
|
-
}
|
|
575
|
-
const discounted = trustDiscount(sourceOpinion, Math.abs(weight));
|
|
576
|
-
return {
|
|
577
|
-
opinion: cumulativeFusion(targetOpinion, discounted),
|
|
578
|
-
operator: "cumulative_fusion",
|
|
579
|
-
rationale: `Supporting evidence (weight=${weight.toFixed(2)})`
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
},
|
|
583
|
-
depends_on: {
|
|
584
|
-
direction: "incoming",
|
|
585
|
-
handler: (source, target, _weight, metadata) => {
|
|
586
|
-
const sourceOpinion = mkOpinion(source.b, source.d, source.u, source.a);
|
|
587
|
-
const targetOpinion = mkOpinion(target.b, target.d, target.u, target.a);
|
|
588
|
-
if (metadata.conditionalA && metadata.conditionalNotA) {
|
|
589
|
-
return {
|
|
590
|
-
opinion: conditionalDeduction(
|
|
591
|
-
sourceOpinion,
|
|
592
|
-
mkOpinion(
|
|
593
|
-
metadata.conditionalA.b,
|
|
594
|
-
metadata.conditionalA.d,
|
|
595
|
-
metadata.conditionalA.u,
|
|
596
|
-
metadata.conditionalA.a
|
|
597
|
-
),
|
|
598
|
-
mkOpinion(
|
|
599
|
-
metadata.conditionalNotA.b,
|
|
600
|
-
metadata.conditionalNotA.d,
|
|
601
|
-
metadata.conditionalNotA.u,
|
|
602
|
-
metadata.conditionalNotA.a
|
|
603
|
-
),
|
|
604
|
-
targetOpinion.a
|
|
605
|
-
),
|
|
606
|
-
operator: "conditional_deduction",
|
|
607
|
-
rationale: `Conditional deduction: prerequisite at ${project(
|
|
608
|
-
sourceOpinion
|
|
609
|
-
).toFixed(2)}`
|
|
610
|
-
};
|
|
611
|
-
}
|
|
612
|
-
return dampedDependencyCascade(
|
|
613
|
-
sourceOpinion,
|
|
614
|
-
targetOpinion,
|
|
615
|
-
metadata.propagation ?? "continuous"
|
|
616
|
-
);
|
|
617
|
-
}
|
|
618
|
-
},
|
|
619
|
-
derived_from: {
|
|
620
|
-
direction: "incoming",
|
|
621
|
-
handler: (_source, target) => ({
|
|
622
|
-
opinion: target,
|
|
623
|
-
operator: "trust_discount",
|
|
624
|
-
rationale: "Provenance edge \u2014 no confidence propagation"
|
|
625
|
-
})
|
|
626
|
-
},
|
|
627
|
-
contains: {
|
|
628
|
-
direction: "outgoing",
|
|
629
|
-
handler: (_source, target) => ({
|
|
630
|
-
opinion: target,
|
|
631
|
-
operator: "trust_discount",
|
|
632
|
-
rationale: "Containment edge \u2014 no confidence propagation"
|
|
633
|
-
})
|
|
634
|
-
},
|
|
635
|
-
tests: {
|
|
636
|
-
direction: "outgoing",
|
|
637
|
-
handler: (_source, target) => ({
|
|
638
|
-
opinion: target,
|
|
639
|
-
operator: "trust_discount",
|
|
640
|
-
rationale: "Testing edge \u2014 no confidence propagation"
|
|
641
|
-
})
|
|
642
|
-
}
|
|
643
|
-
};
|
|
644
|
-
var PROPAGATION_TRAVERSAL_SPECS = Object.entries(EDGE_PROPAGATION_RULES).map(([edgeType, rule]) => ({
|
|
645
|
-
edgeType,
|
|
646
|
-
direction: rule.direction
|
|
647
|
-
}));
|
|
648
|
-
function isPropagationEdgeType(edgeType) {
|
|
649
|
-
return edgeType in EDGE_PROPAGATION_RULES;
|
|
650
|
-
}
|
|
651
|
-
function getPropagationTraversalSpecs() {
|
|
652
|
-
return PROPAGATION_TRAVERSAL_SPECS;
|
|
653
|
-
}
|
|
654
|
-
function propagateThroughEdge(sourceOpinion, targetOpinion, edgeType, weight = 1, metadata = {}) {
|
|
655
|
-
const handler = isPropagationEdgeType(edgeType) ? EDGE_PROPAGATION_RULES[edgeType].handler : void 0;
|
|
656
|
-
if (!handler) {
|
|
657
|
-
return {
|
|
658
|
-
opinion: targetOpinion,
|
|
659
|
-
operator: "no_op",
|
|
660
|
-
rationale: `Unknown edge type: ${edgeType} \u2014 no propagation`
|
|
661
|
-
};
|
|
662
|
-
}
|
|
663
|
-
return handler(sourceOpinion, targetOpinion, weight, metadata);
|
|
664
|
-
}
|
|
665
|
-
function propagateAllEdges(currentOpinion, incomingEdges) {
|
|
666
|
-
let result = currentOpinion;
|
|
667
|
-
const rationales = [];
|
|
668
|
-
for (const edgeType of ["depends_on", "informs", "supports"]) {
|
|
669
|
-
for (const incoming of incomingEdges.filter((edge) => edge.edgeType === edgeType)) {
|
|
670
|
-
const propagated = propagateThroughEdge(
|
|
671
|
-
incoming.sourceOpinion,
|
|
672
|
-
result,
|
|
673
|
-
incoming.edgeType,
|
|
674
|
-
incoming.weight,
|
|
675
|
-
incoming.metadata
|
|
676
|
-
);
|
|
677
|
-
result = propagated.opinion;
|
|
678
|
-
rationales.push(propagated.rationale);
|
|
679
|
-
}
|
|
629
|
+
function parseMarketIndexComparatorConfig(value) {
|
|
630
|
+
if (!isRecord(value)) {
|
|
631
|
+
throw new Error(
|
|
632
|
+
"market_index_comparator requires condition.evaluatorConfig with subjectValue/benchmarkValue/operator/threshold."
|
|
633
|
+
);
|
|
680
634
|
}
|
|
635
|
+
const config = value;
|
|
681
636
|
return {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
637
|
+
subject: typeof config.subject === "string" && config.subject.length > 0 ? config.subject : void 0,
|
|
638
|
+
subjectValue: pickFiniteNumber(config, ["subjectValue", "leftValue"]),
|
|
639
|
+
primaryValue: pickFiniteNumber(config, ["primaryValue"]),
|
|
640
|
+
benchmark: typeof config.benchmark === "string" && config.benchmark.length > 0 ? config.benchmark : void 0,
|
|
641
|
+
benchmarkValue: pickFiniteNumber(config, ["benchmarkValue", "rightValue"]),
|
|
642
|
+
comparisonValue: pickFiniteNumber(config, ["comparisonValue"]),
|
|
643
|
+
operator: parseComparisonOperator(
|
|
644
|
+
config.operator,
|
|
645
|
+
BUILT_IN_MARKET_INDEX_COMPARATOR
|
|
646
|
+
),
|
|
647
|
+
threshold: parseNumericThreshold(
|
|
648
|
+
config.threshold,
|
|
649
|
+
BUILT_IN_MARKET_INDEX_COMPARATOR
|
|
650
|
+
)
|
|
685
651
|
};
|
|
686
652
|
}
|
|
687
653
|
|
|
688
|
-
// src/v1/operations/
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
return Math.max(0, Math.min(1, value));
|
|
692
|
-
}
|
|
693
|
-
function clampNonNegative(value) {
|
|
694
|
-
return Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
654
|
+
// src/v1/operations/scoring.ts
|
|
655
|
+
function finiteNumber(value) {
|
|
656
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
695
657
|
}
|
|
696
|
-
function
|
|
697
|
-
|
|
698
|
-
|
|
658
|
+
function requiredOpinionNumber(label, ...values) {
|
|
659
|
+
for (const value of values) {
|
|
660
|
+
const numberValue = finiteNumber(value);
|
|
661
|
+
if (numberValue !== void 0) {
|
|
662
|
+
return numberValue;
|
|
663
|
+
}
|
|
699
664
|
}
|
|
700
|
-
|
|
665
|
+
throw new Error(`Opinion record is missing required ${label}.`);
|
|
701
666
|
}
|
|
702
|
-
function
|
|
703
|
-
|
|
704
|
-
return [];
|
|
705
|
-
}
|
|
706
|
-
if (baseRate.length !== size) {
|
|
707
|
-
throw new Error(
|
|
708
|
-
`Base-rate vector length ${baseRate.length} must match evidence vector length ${size}.`
|
|
709
|
-
);
|
|
710
|
-
}
|
|
711
|
-
const normalized = baseRate.map((value) => clampNonNegative(value));
|
|
712
|
-
const total = normalized.reduce((sum, value) => sum + value, 0);
|
|
713
|
-
if (total === 0) {
|
|
714
|
-
throw new Error("Base-rate vector must contain at least one positive value.");
|
|
715
|
-
}
|
|
716
|
-
return normalized.map((value) => value / total);
|
|
667
|
+
function clamp01(value) {
|
|
668
|
+
return Math.max(0, Math.min(1, value));
|
|
717
669
|
}
|
|
718
|
-
function
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
const totalEvidence = evidence.reduce((sum, value) => sum + value, 0);
|
|
723
|
-
const denominator = totalEvidence + safeWeight;
|
|
724
|
-
if (denominator === 0) {
|
|
725
|
-
return {
|
|
726
|
-
b: evidence.map(() => 0),
|
|
727
|
-
u: 1,
|
|
728
|
-
a: normalizedBaseRate
|
|
729
|
-
};
|
|
730
|
-
}
|
|
670
|
+
function confidenceFromOpinion(opinion2) {
|
|
671
|
+
return clamp01(opinion2.b + opinion2.a * opinion2.u);
|
|
672
|
+
}
|
|
673
|
+
function toStoredOpinionFields(opinion2) {
|
|
731
674
|
return {
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
675
|
+
belief: opinion2.b,
|
|
676
|
+
disbelief: opinion2.d,
|
|
677
|
+
uncertainty: opinion2.u,
|
|
678
|
+
baseRate: opinion2.a
|
|
735
679
|
};
|
|
736
680
|
}
|
|
737
|
-
function
|
|
738
|
-
const
|
|
739
|
-
[alpha, beta],
|
|
740
|
-
nonInformativeWeight,
|
|
741
|
-
[clamp012(baseRate), 1 - clamp012(baseRate)]
|
|
742
|
-
);
|
|
681
|
+
function readOpinionFromRecord(source) {
|
|
682
|
+
const record = source && typeof source === "object" ? source : {};
|
|
743
683
|
return mkOpinion(
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
684
|
+
requiredOpinionNumber(
|
|
685
|
+
"belief",
|
|
686
|
+
record.b,
|
|
687
|
+
record.belief,
|
|
688
|
+
record.slBelief,
|
|
689
|
+
record.opinion_b
|
|
690
|
+
),
|
|
691
|
+
requiredOpinionNumber(
|
|
692
|
+
"disbelief",
|
|
693
|
+
record.d,
|
|
694
|
+
record.disbelief,
|
|
695
|
+
record.slDisbelief,
|
|
696
|
+
record.opinion_d
|
|
697
|
+
),
|
|
698
|
+
requiredOpinionNumber(
|
|
699
|
+
"uncertainty",
|
|
700
|
+
record.u,
|
|
701
|
+
record.uncertainty,
|
|
702
|
+
record.slUncertainty,
|
|
703
|
+
record.opinion_u
|
|
704
|
+
),
|
|
705
|
+
requiredOpinionNumber(
|
|
706
|
+
"baseRate",
|
|
707
|
+
record.a,
|
|
708
|
+
record.baseRate,
|
|
709
|
+
record.slBaseRate,
|
|
710
|
+
record.opinion_a
|
|
711
|
+
)
|
|
748
712
|
);
|
|
749
713
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
714
|
+
function opinionFromScalar(value, mode, options) {
|
|
715
|
+
const clampedValue = clamp01(value);
|
|
716
|
+
const baseRate = options?.baseRate === void 0 ? void 0 : clamp01(options.baseRate);
|
|
717
|
+
switch (mode) {
|
|
718
|
+
case "base_rate":
|
|
719
|
+
return mkOpinion(0, 0, 1, clampedValue);
|
|
720
|
+
case "dogmatic":
|
|
721
|
+
if (baseRate === void 0) {
|
|
722
|
+
throw new Error(
|
|
723
|
+
'opinionFromScalar(value, "dogmatic") requires options.baseRate.'
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
return mkOpinion(clampedValue, 1 - clampedValue, 0, baseRate);
|
|
727
|
+
case "projected_with_u": {
|
|
728
|
+
if (baseRate === void 0) {
|
|
729
|
+
throw new Error(
|
|
730
|
+
'opinionFromScalar(value, "projected_with_u") requires options.baseRate.'
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
const uncertainty = options?.uncertainty;
|
|
734
|
+
if (uncertainty === void 0) {
|
|
735
|
+
throw new Error(
|
|
736
|
+
'opinionFromScalar(value, "projected_with_u") requires options.uncertainty.'
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
const clampedUncertainty = clamp01(uncertainty);
|
|
740
|
+
const evidenceWeight = 1 - clampedUncertainty;
|
|
741
|
+
return mkOpinion(
|
|
742
|
+
clampedValue * evidenceWeight,
|
|
743
|
+
(1 - clampedValue) * evidenceWeight,
|
|
744
|
+
clampedUncertainty,
|
|
745
|
+
baseRate
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
default:
|
|
749
|
+
throw new Error(`Unsupported opinionFromScalar mode: ${mode}`);
|
|
750
|
+
}
|
|
762
751
|
}
|
|
763
|
-
function
|
|
764
|
-
return
|
|
765
|
-
priorConfidence,
|
|
766
|
-
priorWeight,
|
|
767
|
-
newAssessment,
|
|
768
|
-
newWeight,
|
|
769
|
-
baseRate: options?.baseRate,
|
|
770
|
-
nonInformativeWeight: options?.nonInformativeWeight
|
|
771
|
-
});
|
|
752
|
+
function toDogmaticOpinion(confidence, baseRate) {
|
|
753
|
+
return opinionFromScalar(confidence, "dogmatic", { baseRate });
|
|
772
754
|
}
|
|
773
|
-
function
|
|
774
|
-
return
|
|
755
|
+
function hasProjectedOpinionChanged(current, next, tolerance = 0.01) {
|
|
756
|
+
return Math.abs(confidenceFromOpinion(next) - confidenceFromOpinion(current)) >= tolerance;
|
|
775
757
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
758
|
+
|
|
759
|
+
// src/v1/operations/contradiction/detectTupleContradiction.ts
|
|
760
|
+
var DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD = 0.7;
|
|
761
|
+
var DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD = 0.7;
|
|
762
|
+
function normalizeTupleContradictionPolicy(policy = {}) {
|
|
763
|
+
return {
|
|
764
|
+
beliefThreshold: clamp01(
|
|
765
|
+
policy.beliefThreshold ?? DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD
|
|
766
|
+
),
|
|
767
|
+
disbeliefThreshold: clamp01(
|
|
768
|
+
policy.disbeliefThreshold ?? DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD
|
|
769
|
+
)
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
function detectTupleContradiction(opinion2, tauB = DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD, tauD = DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD) {
|
|
773
|
+
return opinion2.b > tauB && opinion2.d > tauD;
|
|
774
|
+
}
|
|
775
|
+
function evaluateTupleContradictionTransition(args) {
|
|
776
|
+
const policy = normalizeTupleContradictionPolicy(args.policy);
|
|
777
|
+
const tupleContradicted = detectTupleContradiction(
|
|
778
|
+
args.opinion,
|
|
779
|
+
policy.beliefThreshold,
|
|
780
|
+
policy.disbeliefThreshold
|
|
784
781
|
);
|
|
782
|
+
const previousTupleContradicted = Boolean(args.previousTupleContradicted);
|
|
783
|
+
return {
|
|
784
|
+
tupleContradicted,
|
|
785
|
+
crossedIntoTupleContradiction: !previousTupleContradicted && tupleContradicted,
|
|
786
|
+
crossedOutOfTupleContradiction: previousTupleContradicted && !tupleContradicted,
|
|
787
|
+
policy
|
|
788
|
+
};
|
|
785
789
|
}
|
|
786
790
|
|
|
787
791
|
// src/v1/operations/dynamics/decay.ts
|
|
@@ -899,6 +903,102 @@ function lifecycleMultiplier(status) {
|
|
|
899
903
|
}
|
|
900
904
|
return 1;
|
|
901
905
|
}
|
|
906
|
+
function resolveLifecycleRescore(context) {
|
|
907
|
+
if (context.isOverdue && context.lifecycleStatus === "assumption") {
|
|
908
|
+
return {
|
|
909
|
+
priority: "critical",
|
|
910
|
+
reason: `Untested assumption is stale (${Math.round(context.decayState.ageDays)}d) \u2014 validate or supersede`
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
if (context.isOverdue && context.lifecycleStatus === "hypothesis" || context.lifecycleStatus === "hypothesis" && context.daysUntilRescore < 7) {
|
|
914
|
+
return {
|
|
915
|
+
priority: "high",
|
|
916
|
+
reason: `Hypothesis aging without validation (${Math.round(context.decayState.ageDays)}d) \u2014 run/finish sprint testing`
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
function resolveDeadlineRescore(context) {
|
|
921
|
+
const urgency = context.decayState.urgency;
|
|
922
|
+
if (urgency?.isOverdue) {
|
|
923
|
+
return {
|
|
924
|
+
priority: "critical",
|
|
925
|
+
reason: `Prediction deadline passed ${Math.abs(urgency.daysToDeadline)}d ago \u2014 validate or archive`
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
if (urgency?.urgencyTier.label === "critical") {
|
|
929
|
+
return {
|
|
930
|
+
priority: "critical",
|
|
931
|
+
reason: `Deadline in ${urgency.daysToDeadline}d \u2014 needs immediate rescoring`
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
if (urgency?.urgencyTier.label === "imminent") {
|
|
935
|
+
return {
|
|
936
|
+
priority: "high",
|
|
937
|
+
reason: `Deadline in ${urgency.daysToDeadline}d \u2014 weekly rescoring required`
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
function resolveStalenessRescore(context) {
|
|
942
|
+
if (context.isOverdue && context.decayState.baseTier.label === "expired") {
|
|
943
|
+
return {
|
|
944
|
+
priority: "critical",
|
|
945
|
+
reason: `Not scored in ${Math.round(context.decayState.ageDays)}d \u2014 confidence severely degraded`
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
if (context.isOverdue && context.decayState.baseTier.label === "stale") {
|
|
949
|
+
return {
|
|
950
|
+
priority: "high",
|
|
951
|
+
reason: `Stale (${Math.round(context.decayState.ageDays)}d since scoring) \u2014 evidence update required`
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
if (context.isOverdue) {
|
|
955
|
+
return {
|
|
956
|
+
priority: "high",
|
|
957
|
+
reason: `Rescore overdue by ${Math.abs(Math.round(context.daysUntilRescore))}d`
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
function resolveRoutineRescore(context) {
|
|
962
|
+
if (context.temporalNature === "forecast" && context.daysUntilRescore < 7) {
|
|
963
|
+
return {
|
|
964
|
+
priority: "medium",
|
|
965
|
+
reason: `Forecast belief \u2014 next rescore due in ${Math.round(context.daysUntilRescore)}d`
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
if (context.daysUntilRescore < 14) {
|
|
969
|
+
return {
|
|
970
|
+
priority: "medium",
|
|
971
|
+
reason: `Rescore due in ${Math.round(context.daysUntilRescore)}d`
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
return {
|
|
975
|
+
priority: "low",
|
|
976
|
+
reason: `On schedule \u2014 next rescore in ${Math.round(context.daysUntilRescore)}d`
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
function escalateHighConfidenceRescore(result, context) {
|
|
980
|
+
if ((context.confidence ?? 0) >= 0.8 && context.decayState.baseTier.label !== "fresh" && result.priority === "medium") {
|
|
981
|
+
return {
|
|
982
|
+
priority: "high",
|
|
983
|
+
reason: `High-confidence belief (${((context.confidence ?? 0) * 100).toFixed(0)}%) aging without rescore \u2014 ${result.reason}`
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
return result;
|
|
987
|
+
}
|
|
988
|
+
var RESCORING_RULES = [
|
|
989
|
+
resolveLifecycleRescore,
|
|
990
|
+
resolveDeadlineRescore,
|
|
991
|
+
resolveStalenessRescore
|
|
992
|
+
];
|
|
993
|
+
function resolveRescoringPriority(context) {
|
|
994
|
+
for (const rule of RESCORING_RULES) {
|
|
995
|
+
const result = rule(context);
|
|
996
|
+
if (result) {
|
|
997
|
+
return escalateHighConfidenceRescore(result, context);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
return escalateHighConfidenceRescore(resolveRoutineRescore(context), context);
|
|
1001
|
+
}
|
|
902
1002
|
function computeBaseDecay(lastScoredAt) {
|
|
903
1003
|
const ageDays = (Date.now() - lastScoredAt) / (1e3 * 60 * 60 * 24);
|
|
904
1004
|
let tier = DECAY_TIERS.EXPIRED;
|
|
@@ -970,46 +1070,14 @@ function getRescoringSchedule(opts) {
|
|
|
970
1070
|
);
|
|
971
1071
|
const daysUntilRescore = (decayState.rescoreByDate - Date.now()) / (1e3 * 60 * 60 * 24);
|
|
972
1072
|
const isOverdue = daysUntilRescore < 0;
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
} else if (decayState.urgency?.isOverdue) {
|
|
982
|
-
priority = "critical";
|
|
983
|
-
reason = `Prediction deadline passed ${Math.abs(decayState.urgency.daysToDeadline)}d ago \u2014 validate or archive`;
|
|
984
|
-
} else if (isOverdue && decayState.baseTier.label === "expired") {
|
|
985
|
-
priority = "critical";
|
|
986
|
-
reason = `Not scored in ${Math.round(decayState.ageDays)}d \u2014 confidence severely degraded`;
|
|
987
|
-
} else if (isOverdue && decayState.baseTier.label === "stale") {
|
|
988
|
-
priority = "high";
|
|
989
|
-
reason = `Stale (${Math.round(decayState.ageDays)}d since scoring) \u2014 evidence update required`;
|
|
990
|
-
} else if (decayState.urgency && decayState.urgency.urgencyTier.label === "critical") {
|
|
991
|
-
priority = "critical";
|
|
992
|
-
reason = `Deadline in ${decayState.urgency.daysToDeadline}d \u2014 needs immediate rescoring`;
|
|
993
|
-
} else if (decayState.urgency && decayState.urgency.urgencyTier.label === "imminent") {
|
|
994
|
-
priority = "high";
|
|
995
|
-
reason = `Deadline in ${decayState.urgency.daysToDeadline}d \u2014 weekly rescoring required`;
|
|
996
|
-
} else if (isOverdue) {
|
|
997
|
-
priority = "high";
|
|
998
|
-
reason = `Rescore overdue by ${Math.abs(Math.round(daysUntilRescore))}d`;
|
|
999
|
-
} else if (opts.temporalNature === "forecast" && daysUntilRescore < 7) {
|
|
1000
|
-
priority = "medium";
|
|
1001
|
-
reason = `Forecast belief \u2014 next rescore due in ${Math.round(daysUntilRescore)}d`;
|
|
1002
|
-
} else if (daysUntilRescore < 14) {
|
|
1003
|
-
priority = "medium";
|
|
1004
|
-
reason = `Rescore due in ${Math.round(daysUntilRescore)}d`;
|
|
1005
|
-
} else {
|
|
1006
|
-
priority = "low";
|
|
1007
|
-
reason = `On schedule \u2014 next rescore in ${Math.round(daysUntilRescore)}d`;
|
|
1008
|
-
}
|
|
1009
|
-
if ((opts.confidence ?? 0) >= 0.8 && decayState.baseTier.label !== "fresh" && priority === "medium") {
|
|
1010
|
-
priority = "high";
|
|
1011
|
-
reason = `High-confidence belief (${((opts.confidence ?? 0) * 100).toFixed(0)}%) aging without rescore \u2014 ${reason}`;
|
|
1012
|
-
}
|
|
1073
|
+
const { priority, reason } = resolveRescoringPriority({
|
|
1074
|
+
confidence: opts.confidence,
|
|
1075
|
+
daysUntilRescore,
|
|
1076
|
+
decayState,
|
|
1077
|
+
isOverdue,
|
|
1078
|
+
lifecycleStatus,
|
|
1079
|
+
temporalNature: opts.temporalNature
|
|
1080
|
+
});
|
|
1013
1081
|
return {
|
|
1014
1082
|
nextRescoreBy: decayState.rescoreByDate,
|
|
1015
1083
|
daysUntilRescore: Math.round(daysUntilRescore),
|
|
@@ -1023,7 +1091,7 @@ function decay(current, daysSinceLastUpdate, halfLifeDays = 90) {
|
|
|
1023
1091
|
if (daysSinceLastUpdate <= 0) {
|
|
1024
1092
|
return current;
|
|
1025
1093
|
}
|
|
1026
|
-
const decayFactor =
|
|
1094
|
+
const decayFactor = 0.5 ** (daysSinceLastUpdate / halfLifeDays);
|
|
1027
1095
|
const certainty = current.b + current.d;
|
|
1028
1096
|
const lostCertainty = certainty - certainty * decayFactor;
|
|
1029
1097
|
return opinion(
|
|
@@ -1034,316 +1102,375 @@ function decay(current, daysSinceLastUpdate, halfLifeDays = 90) {
|
|
|
1034
1102
|
);
|
|
1035
1103
|
}
|
|
1036
1104
|
|
|
1037
|
-
// src/v1/operations/
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
return crypto.randomUUID();
|
|
1047
|
-
}
|
|
1048
|
-
return `contract-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
1049
|
-
}
|
|
1050
|
-
function isRecord(value) {
|
|
1051
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1052
|
-
}
|
|
1053
|
-
function deriveContractStatus(result, currentStatus) {
|
|
1054
|
-
if (currentStatus === "archived") {
|
|
1055
|
-
return currentStatus;
|
|
1056
|
-
}
|
|
1057
|
-
switch (result) {
|
|
1058
|
-
case "confirmed":
|
|
1059
|
-
return "satisfied";
|
|
1060
|
-
case "disconfirmed":
|
|
1061
|
-
return "violated";
|
|
1062
|
-
case "expired":
|
|
1063
|
-
return "expired";
|
|
1064
|
-
default:
|
|
1065
|
-
return currentStatus === "satisfied" || currentStatus === "violated" || currentStatus === "expired" ? "active" : currentStatus;
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
function deriveVerificationTrigger(result) {
|
|
1069
|
-
switch (result) {
|
|
1070
|
-
case "confirmed":
|
|
1071
|
-
return "verification_confirmed";
|
|
1072
|
-
case "disconfirmed":
|
|
1073
|
-
return "verification_disconfirmed";
|
|
1074
|
-
case "expired":
|
|
1075
|
-
return "verification_expired";
|
|
1076
|
-
case "partial":
|
|
1077
|
-
return "verification_partial";
|
|
1078
|
-
default:
|
|
1079
|
-
return null;
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
function deriveContractModulationPlan(args) {
|
|
1083
|
-
const trigger = deriveVerificationTrigger(args.result);
|
|
1084
|
-
if (!trigger) {
|
|
1085
|
-
return null;
|
|
1086
|
-
}
|
|
1087
|
-
if (args.result === "confirmed") {
|
|
1088
|
-
const rawNext = args.currentConfidence + args.modulation.onConfirmed.delta;
|
|
1089
|
-
const confidenceAfter = clampConfidence(
|
|
1090
|
-
Math.min(
|
|
1091
|
-
args.modulation.onConfirmed.ceiling ?? Number.POSITIVE_INFINITY,
|
|
1092
|
-
rawNext
|
|
1093
|
-
)
|
|
1094
|
-
);
|
|
1095
|
-
return {
|
|
1096
|
-
trigger,
|
|
1097
|
-
confidenceBefore: args.currentConfidence,
|
|
1098
|
-
confidenceAfter,
|
|
1099
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
1100
|
-
};
|
|
1101
|
-
}
|
|
1102
|
-
if (args.result === "disconfirmed") {
|
|
1103
|
-
const rawNext = args.currentConfidence + args.modulation.onDisconfirmed.delta;
|
|
1104
|
-
const confidenceAfter = clampConfidence(
|
|
1105
|
-
Math.max(args.modulation.onDisconfirmed.floor ?? 0, rawNext)
|
|
1106
|
-
);
|
|
1107
|
-
return {
|
|
1108
|
-
trigger,
|
|
1109
|
-
confidenceBefore: args.currentConfidence,
|
|
1110
|
-
confidenceAfter,
|
|
1111
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
1112
|
-
};
|
|
1113
|
-
}
|
|
1114
|
-
if (args.result === "expired" && args.modulation.onExpired) {
|
|
1115
|
-
const confidenceAfter = clampConfidence(
|
|
1116
|
-
args.currentConfidence + args.modulation.onExpired.delta
|
|
1117
|
-
);
|
|
1118
|
-
return {
|
|
1119
|
-
trigger,
|
|
1120
|
-
confidenceBefore: args.currentConfidence,
|
|
1121
|
-
confidenceAfter,
|
|
1122
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
1123
|
-
};
|
|
1124
|
-
}
|
|
1125
|
-
if (args.result === "partial" && args.modulation.onPartial) {
|
|
1126
|
-
if ((args.resultConfidence ?? 0) < args.modulation.onPartial.threshold) {
|
|
1127
|
-
return null;
|
|
1128
|
-
}
|
|
1129
|
-
const confidenceAfter = clampConfidence(
|
|
1130
|
-
args.currentConfidence + args.modulation.onPartial.delta
|
|
1105
|
+
// src/v1/operations/dynamics/defeat.ts
|
|
1106
|
+
function applyNegativeSupport(source, target, weight, metadata = {}) {
|
|
1107
|
+
const sourceOpinion = mkOpinion(source.b, source.d, source.u, source.a);
|
|
1108
|
+
const targetOpinion = mkOpinion(target.b, target.d, target.u, target.a);
|
|
1109
|
+
if (metadata.constraint === "xor") {
|
|
1110
|
+
const result = constraintFusion(
|
|
1111
|
+
sourceOpinion,
|
|
1112
|
+
targetOpinion,
|
|
1113
|
+
metadata.normalization ?? "pressure"
|
|
1131
1114
|
);
|
|
1132
1115
|
return {
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1116
|
+
opinion: result.o2,
|
|
1117
|
+
operator: "constraint_fusion",
|
|
1118
|
+
rationale: `XOR constraint: source belief at ${project(
|
|
1119
|
+
sourceOpinion
|
|
1120
|
+
).toFixed(2)} pressures target`
|
|
1137
1121
|
};
|
|
1138
1122
|
}
|
|
1139
|
-
|
|
1140
|
-
}
|
|
1141
|
-
function createInheritedContractRecord(contract, args) {
|
|
1123
|
+
const discounted = trustDiscount(negate(sourceOpinion), Math.abs(weight));
|
|
1142
1124
|
return {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
direction: contract.direction,
|
|
1149
|
-
condition: contract.condition,
|
|
1150
|
-
deadline: contract.deadline,
|
|
1151
|
-
compositeOf: contract.compositeOf,
|
|
1152
|
-
compositeOperator: contract.compositeOperator,
|
|
1153
|
-
modulation: contract.modulation,
|
|
1154
|
-
evaluationSchedule: contract.evaluationSchedule,
|
|
1155
|
-
periodicIntervalMs: contract.periodicIntervalMs,
|
|
1156
|
-
status: "active",
|
|
1157
|
-
lineageSource: "inherited",
|
|
1158
|
-
inheritedFromContractId: contract.contractId,
|
|
1159
|
-
inheritedFromBeliefNodeId: contract.beliefNodeId,
|
|
1160
|
-
inheritedAt: args.now,
|
|
1161
|
-
topicId: args.topicId,
|
|
1162
|
-
createdAt: args.now,
|
|
1163
|
-
createdBy: args.createdBy,
|
|
1164
|
-
updatedAt: args.now
|
|
1125
|
+
opinion: cumulativeFusion(targetOpinion, discounted),
|
|
1126
|
+
operator: "cumulative_fusion",
|
|
1127
|
+
rationale: `Contradicting evidence (weight=${weight.toFixed(
|
|
1128
|
+
2
|
|
1129
|
+
)}) from source at ${project(sourceOpinion).toFixed(2)}`
|
|
1165
1130
|
};
|
|
1166
1131
|
}
|
|
1167
|
-
function
|
|
1168
|
-
|
|
1132
|
+
function applyNegativeEvidence(source, target, weight) {
|
|
1133
|
+
const sourceOpinion = mkOpinion(source.b, source.d, source.u, source.a);
|
|
1134
|
+
const targetOpinion = mkOpinion(target.b, target.d, target.u, target.a);
|
|
1135
|
+
const discounted = trustDiscount(negate(sourceOpinion), Math.abs(weight));
|
|
1136
|
+
return {
|
|
1137
|
+
opinion: cumulativeFusion(targetOpinion, discounted),
|
|
1138
|
+
operator: "cumulative_fusion",
|
|
1139
|
+
rationale: `Contradicting evidence (weight=${weight.toFixed(2)})`
|
|
1140
|
+
};
|
|
1169
1141
|
}
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1142
|
+
|
|
1143
|
+
// src/v1/operations/dynamics/cascade.ts
|
|
1144
|
+
function dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode = "continuous", threshold = 0.3) {
|
|
1145
|
+
const dependencyProjection = project(
|
|
1146
|
+
mkOpinion(
|
|
1147
|
+
dependencyOpinion.b,
|
|
1148
|
+
dependencyOpinion.d,
|
|
1149
|
+
dependencyOpinion.u,
|
|
1150
|
+
dependencyOpinion.a
|
|
1151
|
+
)
|
|
1176
1152
|
);
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
for (const key of keys) {
|
|
1186
|
-
const value = config[key];
|
|
1187
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1188
|
-
return value;
|
|
1153
|
+
if (mode === "threshold") {
|
|
1154
|
+
if (dependencyProjection < threshold) {
|
|
1155
|
+
return opinion(
|
|
1156
|
+
0,
|
|
1157
|
+
beliefOpinion.d + beliefOpinion.b * 0.5,
|
|
1158
|
+
0.5,
|
|
1159
|
+
beliefOpinion.a
|
|
1160
|
+
);
|
|
1189
1161
|
}
|
|
1162
|
+
return beliefOpinion;
|
|
1190
1163
|
}
|
|
1191
|
-
|
|
1164
|
+
const dampingFactor = dependencyProjection ** 0.5;
|
|
1165
|
+
return opinion(
|
|
1166
|
+
beliefOpinion.b * dampingFactor,
|
|
1167
|
+
beliefOpinion.d + beliefOpinion.b * (1 - dampingFactor) * 0.3,
|
|
1168
|
+
beliefOpinion.u + beliefOpinion.b * (1 - dampingFactor) * 0.7,
|
|
1169
|
+
beliefOpinion.a
|
|
1170
|
+
);
|
|
1192
1171
|
}
|
|
1193
|
-
function
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1172
|
+
function dampedDependencyCascade(dependencyOpinion, beliefOpinion, mode = "continuous") {
|
|
1173
|
+
return {
|
|
1174
|
+
opinion: dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode),
|
|
1175
|
+
operator: "dependency_cascade",
|
|
1176
|
+
rationale: `Damped dependency cascade (${mode}): prerequisite at ${project(
|
|
1177
|
+
mkOpinion(
|
|
1178
|
+
dependencyOpinion.b,
|
|
1179
|
+
dependencyOpinion.d,
|
|
1180
|
+
dependencyOpinion.u,
|
|
1181
|
+
dependencyOpinion.a
|
|
1182
|
+
)
|
|
1183
|
+
).toFixed(2)}`
|
|
1184
|
+
};
|
|
1202
1185
|
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1186
|
+
|
|
1187
|
+
// src/v1/operations/dynamics/propagation.ts
|
|
1188
|
+
var EDGE_PROPAGATION_RULES = {
|
|
1189
|
+
supports: {
|
|
1190
|
+
direction: "outgoing",
|
|
1191
|
+
handler: (source, target, weight, metadata) => {
|
|
1192
|
+
const sourceOpinion = mkOpinion(source.b, source.d, source.u, source.a);
|
|
1193
|
+
const targetOpinion = mkOpinion(target.b, target.d, target.u, target.a);
|
|
1194
|
+
if (weight < 0) {
|
|
1195
|
+
return applyNegativeSupport(
|
|
1196
|
+
sourceOpinion,
|
|
1197
|
+
targetOpinion,
|
|
1198
|
+
weight,
|
|
1199
|
+
metadata
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
const discounted = trustDiscount(sourceOpinion, weight);
|
|
1203
|
+
return {
|
|
1204
|
+
opinion: cumulativeFusion(targetOpinion, discounted),
|
|
1205
|
+
operator: "cumulative_fusion",
|
|
1206
|
+
rationale: `Supporting evidence (weight=${weight.toFixed(
|
|
1207
|
+
2
|
|
1208
|
+
)}) from source at ${project(sourceOpinion).toFixed(2)}`
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
},
|
|
1212
|
+
informs: {
|
|
1213
|
+
direction: "outgoing",
|
|
1214
|
+
handler: (source, target, weight) => {
|
|
1215
|
+
const sourceOpinion = mkOpinion(source.b, source.d, source.u, source.a);
|
|
1216
|
+
const targetOpinion = mkOpinion(target.b, target.d, target.u, target.a);
|
|
1217
|
+
if (weight < 0) {
|
|
1218
|
+
return applyNegativeEvidence(sourceOpinion, targetOpinion, weight);
|
|
1219
|
+
}
|
|
1220
|
+
const discounted = trustDiscount(sourceOpinion, Math.abs(weight));
|
|
1221
|
+
return {
|
|
1222
|
+
opinion: cumulativeFusion(targetOpinion, discounted),
|
|
1223
|
+
operator: "cumulative_fusion",
|
|
1224
|
+
rationale: `Supporting evidence (weight=${weight.toFixed(2)})`
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
},
|
|
1228
|
+
depends_on: {
|
|
1229
|
+
direction: "incoming",
|
|
1230
|
+
handler: (source, target, _weight, metadata) => {
|
|
1231
|
+
const sourceOpinion = mkOpinion(source.b, source.d, source.u, source.a);
|
|
1232
|
+
const targetOpinion = mkOpinion(target.b, target.d, target.u, target.a);
|
|
1233
|
+
if (metadata.conditionalA && metadata.conditionalNotA) {
|
|
1234
|
+
return {
|
|
1235
|
+
opinion: conditionalDeduction(
|
|
1236
|
+
sourceOpinion,
|
|
1237
|
+
mkOpinion(
|
|
1238
|
+
metadata.conditionalA.b,
|
|
1239
|
+
metadata.conditionalA.d,
|
|
1240
|
+
metadata.conditionalA.u,
|
|
1241
|
+
metadata.conditionalA.a
|
|
1242
|
+
),
|
|
1243
|
+
mkOpinion(
|
|
1244
|
+
metadata.conditionalNotA.b,
|
|
1245
|
+
metadata.conditionalNotA.d,
|
|
1246
|
+
metadata.conditionalNotA.u,
|
|
1247
|
+
metadata.conditionalNotA.a
|
|
1248
|
+
),
|
|
1249
|
+
targetOpinion.a
|
|
1250
|
+
),
|
|
1251
|
+
operator: "conditional_deduction",
|
|
1252
|
+
rationale: `Conditional deduction: prerequisite at ${project(
|
|
1253
|
+
sourceOpinion
|
|
1254
|
+
).toFixed(2)}`
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
return dampedDependencyCascade(
|
|
1258
|
+
sourceOpinion,
|
|
1259
|
+
targetOpinion,
|
|
1260
|
+
metadata.propagation ?? "continuous"
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
},
|
|
1264
|
+
derived_from: {
|
|
1265
|
+
direction: "incoming",
|
|
1266
|
+
handler: (_source, target) => ({
|
|
1267
|
+
opinion: target,
|
|
1268
|
+
operator: "trust_discount",
|
|
1269
|
+
rationale: "Provenance edge \u2014 no confidence propagation"
|
|
1270
|
+
})
|
|
1271
|
+
},
|
|
1272
|
+
contains: {
|
|
1273
|
+
direction: "outgoing",
|
|
1274
|
+
handler: (_source, target) => ({
|
|
1275
|
+
opinion: target,
|
|
1276
|
+
operator: "trust_discount",
|
|
1277
|
+
rationale: "Containment edge \u2014 no confidence propagation"
|
|
1278
|
+
})
|
|
1279
|
+
},
|
|
1280
|
+
tests: {
|
|
1281
|
+
direction: "outgoing",
|
|
1282
|
+
handler: (_source, target) => ({
|
|
1283
|
+
opinion: target,
|
|
1284
|
+
operator: "trust_discount",
|
|
1285
|
+
rationale: "Testing edge \u2014 no confidence propagation"
|
|
1286
|
+
})
|
|
1215
1287
|
}
|
|
1216
|
-
|
|
1217
|
-
|
|
1288
|
+
};
|
|
1289
|
+
var PROPAGATION_TRAVERSAL_SPECS = Object.entries(EDGE_PROPAGATION_RULES).map(([edgeType, rule]) => ({
|
|
1290
|
+
edgeType,
|
|
1291
|
+
direction: rule.direction
|
|
1292
|
+
}));
|
|
1293
|
+
function isPropagationEdgeType(edgeType) {
|
|
1294
|
+
return edgeType in EDGE_PROPAGATION_RULES;
|
|
1295
|
+
}
|
|
1296
|
+
function getPropagationTraversalSpecs() {
|
|
1297
|
+
return PROPAGATION_TRAVERSAL_SPECS;
|
|
1298
|
+
}
|
|
1299
|
+
function propagateThroughEdge(sourceOpinion, targetOpinion, edgeType, weight = 1, metadata = {}) {
|
|
1300
|
+
const handler = isPropagationEdgeType(edgeType) ? EDGE_PROPAGATION_RULES[edgeType].handler : void 0;
|
|
1301
|
+
if (!handler) {
|
|
1302
|
+
return {
|
|
1303
|
+
opinion: targetOpinion,
|
|
1304
|
+
operator: "no_op",
|
|
1305
|
+
rationale: `Unknown edge type: ${edgeType} \u2014 no propagation`
|
|
1306
|
+
};
|
|
1218
1307
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1308
|
+
return handler(sourceOpinion, targetOpinion, weight, metadata);
|
|
1309
|
+
}
|
|
1310
|
+
function propagateAllEdges(currentOpinion, incomingEdges) {
|
|
1311
|
+
let result = currentOpinion;
|
|
1312
|
+
const rationales = [];
|
|
1313
|
+
for (const edgeType of ["depends_on", "informs", "supports"]) {
|
|
1314
|
+
for (const incoming of incomingEdges.filter(
|
|
1315
|
+
(edge) => edge.edgeType === edgeType
|
|
1316
|
+
)) {
|
|
1317
|
+
const propagated = propagateThroughEdge(
|
|
1318
|
+
incoming.sourceOpinion,
|
|
1319
|
+
result,
|
|
1320
|
+
incoming.edgeType,
|
|
1321
|
+
incoming.weight,
|
|
1322
|
+
incoming.metadata
|
|
1323
|
+
);
|
|
1324
|
+
result = propagated.opinion;
|
|
1325
|
+
rationales.push(propagated.rationale);
|
|
1326
|
+
}
|
|
1221
1327
|
}
|
|
1222
|
-
const actionParams = isRecord(config.actionParams) ? config.actionParams : void 0;
|
|
1223
1328
|
return {
|
|
1224
|
-
|
|
1225
|
-
operator,
|
|
1226
|
-
|
|
1227
|
-
action: normalizeEvidentialAction(config.action),
|
|
1228
|
-
actionParams: actionParams && (typeof actionParams.targetConfidence === "number" || typeof actionParams.rationale === "string") ? {
|
|
1229
|
-
targetConfidence: typeof actionParams.targetConfidence === "number" ? actionParams.targetConfidence : void 0,
|
|
1230
|
-
rationale: typeof actionParams.rationale === "string" ? actionParams.rationale : void 0
|
|
1231
|
-
} : void 0
|
|
1329
|
+
opinion: result,
|
|
1330
|
+
operator: "cumulative_fusion",
|
|
1331
|
+
rationale: rationales.join("; ")
|
|
1232
1332
|
};
|
|
1233
1333
|
}
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
return left <= right;
|
|
1240
|
-
case "eq":
|
|
1241
|
-
return left === right;
|
|
1242
|
-
case "gt":
|
|
1243
|
-
return left > right;
|
|
1244
|
-
case "lt":
|
|
1245
|
-
return left < right;
|
|
1246
|
-
default:
|
|
1247
|
-
return false;
|
|
1248
|
-
}
|
|
1334
|
+
|
|
1335
|
+
// src/v1/operations/bridge/index.ts
|
|
1336
|
+
var DEFAULT_NON_INFORMATIVE_WEIGHT = 2;
|
|
1337
|
+
function clamp012(value) {
|
|
1338
|
+
return Math.max(0, Math.min(1, value));
|
|
1249
1339
|
}
|
|
1250
|
-
function
|
|
1251
|
-
return
|
|
1340
|
+
function clampNonNegative(value) {
|
|
1341
|
+
return Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
1252
1342
|
}
|
|
1253
|
-
function
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
const clause = `${args.label} observed ${renderedObserved} against ${args.operator} ${renderedThreshold}`;
|
|
1257
|
-
if (args.observedValue === null) {
|
|
1258
|
-
return `${clause}; evaluator returned ${args.result} because no current data was available.`;
|
|
1343
|
+
function normalizeNonInformativeWeight(weight) {
|
|
1344
|
+
if (weight === void 0) {
|
|
1345
|
+
return DEFAULT_NON_INFORMATIVE_WEIGHT;
|
|
1259
1346
|
}
|
|
1260
|
-
return
|
|
1347
|
+
return Number.isFinite(weight) ? Math.max(0, weight) : DEFAULT_NON_INFORMATIVE_WEIGHT;
|
|
1261
1348
|
}
|
|
1262
|
-
function
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
if (args.snapshot.value === null) {
|
|
1266
|
-
return `${clause}; evidential evaluator treated the comparison as unmet, resulting in ${args.result}.`;
|
|
1349
|
+
function normalizeBaseRateVector(baseRate, size) {
|
|
1350
|
+
if (size === 0) {
|
|
1351
|
+
return [];
|
|
1267
1352
|
}
|
|
1268
|
-
|
|
1269
|
-
}
|
|
1270
|
-
function parseMetricCheckerConfig(value) {
|
|
1271
|
-
if (!isRecord(value)) {
|
|
1353
|
+
if (baseRate.length !== size) {
|
|
1272
1354
|
throw new Error(
|
|
1273
|
-
|
|
1355
|
+
`Base-rate vector length ${baseRate.length} must match evidence vector length ${size}.`
|
|
1274
1356
|
);
|
|
1275
1357
|
}
|
|
1276
|
-
const
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
operator: parseComparisonOperator(config.operator, BUILT_IN_METRIC_CHECKER),
|
|
1280
|
-
threshold: parseNumericThreshold(config.threshold, BUILT_IN_METRIC_CHECKER),
|
|
1281
|
-
observedValue: pickFiniteNumber(config, ["observedValue", "value"]),
|
|
1282
|
-
currentValue: pickFiniteNumber(config, ["currentValue"]),
|
|
1283
|
-
metricValue: pickFiniteNumber(config, ["metricValue"]),
|
|
1284
|
-
unit: typeof config.unit === "string" && config.unit.length > 0 ? config.unit : void 0
|
|
1285
|
-
};
|
|
1286
|
-
}
|
|
1287
|
-
function parseReferenceCheckCounterConfig(value) {
|
|
1288
|
-
if (!isRecord(value)) {
|
|
1358
|
+
const normalized = baseRate.map((value) => clampNonNegative(value));
|
|
1359
|
+
const total = normalized.reduce((sum, value) => sum + value, 0);
|
|
1360
|
+
if (total === 0) {
|
|
1289
1361
|
throw new Error(
|
|
1290
|
-
"
|
|
1362
|
+
"Base-rate vector must contain at least one positive value."
|
|
1291
1363
|
);
|
|
1292
1364
|
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1365
|
+
return normalized.map((value) => value / total);
|
|
1366
|
+
}
|
|
1367
|
+
function opinionFromDirichlet(alpha, nonInformativeWeight, baseRate) {
|
|
1368
|
+
const evidence = alpha.map((value) => clampNonNegative(value));
|
|
1369
|
+
const safeWeight = normalizeNonInformativeWeight(nonInformativeWeight);
|
|
1370
|
+
const normalizedBaseRate = normalizeBaseRateVector(baseRate, evidence.length);
|
|
1371
|
+
const totalEvidence = evidence.reduce((sum, value) => sum + value, 0);
|
|
1372
|
+
const denominator = totalEvidence + safeWeight;
|
|
1373
|
+
if (denominator === 0) {
|
|
1374
|
+
return {
|
|
1375
|
+
b: evidence.map(() => 0),
|
|
1376
|
+
u: 1,
|
|
1377
|
+
a: normalizedBaseRate
|
|
1378
|
+
};
|
|
1296
1379
|
}
|
|
1297
1380
|
return {
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
BUILT_IN_REFERENCE_CHECK_COUNTER
|
|
1302
|
-
),
|
|
1303
|
-
threshold: parseNumericThreshold(
|
|
1304
|
-
config.threshold,
|
|
1305
|
-
BUILT_IN_REFERENCE_CHECK_COUNTER
|
|
1306
|
-
),
|
|
1307
|
-
caseSensitive: config.caseSensitive === true
|
|
1381
|
+
b: evidence.map((value) => value / denominator),
|
|
1382
|
+
u: safeWeight / denominator,
|
|
1383
|
+
a: normalizedBaseRate
|
|
1308
1384
|
};
|
|
1309
1385
|
}
|
|
1310
|
-
function
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1386
|
+
function opinionFromBeta(alpha, beta, nonInformativeWeight, baseRate) {
|
|
1387
|
+
const dirichlet = opinionFromDirichlet([alpha, beta], nonInformativeWeight, [
|
|
1388
|
+
clamp012(baseRate),
|
|
1389
|
+
1 - clamp012(baseRate)
|
|
1390
|
+
]);
|
|
1391
|
+
return mkOpinion(
|
|
1392
|
+
dirichlet.b[0] ?? 0,
|
|
1393
|
+
dirichlet.b[1] ?? 0,
|
|
1394
|
+
dirichlet.u,
|
|
1395
|
+
dirichlet.a[0] ?? clamp012(baseRate)
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
// src/v1/operations/dynamics/revision.ts
|
|
1400
|
+
function clamp013(value) {
|
|
1401
|
+
return Math.max(0, Math.min(1, value));
|
|
1402
|
+
}
|
|
1403
|
+
function toEvidence(probability, weight) {
|
|
1404
|
+
const safeProbability = clamp013(probability);
|
|
1405
|
+
const safeWeight = Number.isFinite(weight) ? Math.max(0, weight) : 0;
|
|
1315
1406
|
return {
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
completedAt: pickFiniteNumber(config, ["completedAt"]),
|
|
1319
|
-
observedAt: pickFiniteNumber(config, ["observedAt"]),
|
|
1320
|
-
satisfiedAt: pickFiniteNumber(config, ["satisfiedAt"]),
|
|
1321
|
-
achievedAt: pickFiniteNumber(config, ["achievedAt"])
|
|
1407
|
+
alpha: safeProbability * safeWeight,
|
|
1408
|
+
beta: (1 - safeProbability) * safeWeight
|
|
1322
1409
|
};
|
|
1323
1410
|
}
|
|
1324
|
-
function
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1411
|
+
function bayesianUpdate(priorConfidence, priorWeight, newAssessment, newWeight, options) {
|
|
1412
|
+
return reviseConfidence({
|
|
1413
|
+
priorConfidence,
|
|
1414
|
+
priorWeight,
|
|
1415
|
+
newAssessment,
|
|
1416
|
+
newWeight,
|
|
1417
|
+
baseRate: options?.baseRate,
|
|
1418
|
+
nonInformativeWeight: options?.nonInformativeWeight
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
function reviseConfidence(args) {
|
|
1422
|
+
return project(reviseConfidenceOpinion(args));
|
|
1423
|
+
}
|
|
1424
|
+
function reviseConfidenceOpinion(args) {
|
|
1425
|
+
const priorEvidence = toEvidence(args.priorConfidence, args.priorWeight);
|
|
1426
|
+
const newEvidence = toEvidence(args.newAssessment, args.newWeight ?? 1);
|
|
1427
|
+
return opinionFromBeta(
|
|
1428
|
+
priorEvidence.alpha + newEvidence.alpha,
|
|
1429
|
+
priorEvidence.beta + newEvidence.beta,
|
|
1430
|
+
args.nonInformativeWeight ?? DEFAULT_NON_INFORMATIVE_WEIGHT,
|
|
1431
|
+
args.baseRate
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
function toEpochMs(value) {
|
|
1435
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1436
|
+
return value;
|
|
1329
1437
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1438
|
+
if (value instanceof Date) {
|
|
1439
|
+
const epochMs = value.getTime();
|
|
1440
|
+
return Number.isFinite(epochMs) ? epochMs : void 0;
|
|
1441
|
+
}
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
function normalizeHalfLifeMs(halfLifeMs) {
|
|
1445
|
+
if (halfLifeMs === void 0 || !Number.isFinite(halfLifeMs)) {
|
|
1446
|
+
return 0;
|
|
1447
|
+
}
|
|
1448
|
+
return Math.max(0, halfLifeMs);
|
|
1449
|
+
}
|
|
1450
|
+
function temporalDecay(sourceOpinion, now, decayParams) {
|
|
1451
|
+
const halfLifeMs = normalizeHalfLifeMs(decayParams.halfLifeMs);
|
|
1452
|
+
if (halfLifeMs === 0) {
|
|
1453
|
+
return { ...sourceOpinion };
|
|
1454
|
+
}
|
|
1455
|
+
const nowMs = toEpochMs(now);
|
|
1456
|
+
const referenceTimeMs = toEpochMs(decayParams.referenceTime);
|
|
1457
|
+
if (nowMs === void 0 || referenceTimeMs === void 0) {
|
|
1458
|
+
return { ...sourceOpinion };
|
|
1459
|
+
}
|
|
1460
|
+
const ageMs = Math.max(0, nowMs - referenceTimeMs);
|
|
1461
|
+
if (ageMs === 0) {
|
|
1462
|
+
return { ...sourceOpinion };
|
|
1463
|
+
}
|
|
1464
|
+
const retainedEvidenceWeight = 0.5 ** (ageMs / halfLifeMs);
|
|
1465
|
+
return trustDiscount(
|
|
1466
|
+
mkOpinion(
|
|
1467
|
+
sourceOpinion.b,
|
|
1468
|
+
sourceOpinion.d,
|
|
1469
|
+
sourceOpinion.u,
|
|
1470
|
+
sourceOpinion.a
|
|
1341
1471
|
),
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
BUILT_IN_MARKET_INDEX_COMPARATOR
|
|
1345
|
-
)
|
|
1346
|
-
};
|
|
1472
|
+
retainedEvidenceWeight
|
|
1473
|
+
);
|
|
1347
1474
|
}
|
|
1348
1475
|
|
|
1349
1476
|
// src/v1/operations/lucern.ts
|