@disclosureos/scoring 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +27 -3
- package/dist/compellingness/index.d.ts +51 -1
- package/dist/compellingness/index.js +35 -1
- package/dist/compellingness/index.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +35 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -4,11 +4,34 @@ All notable changes to `@disclosureos/scoring` are documented here. The format i
|
|
|
4
4
|
based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project
|
|
5
5
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 1.1.0
|
|
8
|
+
|
|
9
|
+
### Minor Changes
|
|
10
|
+
|
|
11
|
+
- 19bad3d: Add `calibrationTrust` — calibration provenance from published sensor manifests
|
|
12
|
+
(`@disclosureos/instruments`) as a scoring input. Builds an `evaluatorWeight`
|
|
13
|
+
policy that credits claims whose sensor evidence resolves (via
|
|
14
|
+
`SensorReading.sensorRef`) to a manifest entry with real calibration
|
|
15
|
+
provenance, under an overridable `CALIBRATION_TRUST_WEIGHTS` gradient
|
|
16
|
+
(`unresolved`/`none` 1.0 baseline → `documented` 1.25). Conservative by
|
|
17
|
+
default: unresolved sensors are never penalized, and only the consensus point
|
|
18
|
+
shifts — `range` and `contested` still report the honest spread across claims.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [b373962]
|
|
23
|
+
- Updated dependencies [e6ff99c]
|
|
24
|
+
- @disclosureos/instruments@1.0.0
|
|
25
|
+
- @disclosureos/records@1.1.0
|
|
26
|
+
- @disclosureos/observables@1.0.1
|
|
27
|
+
- @disclosureos/origins@1.0.1
|
|
28
|
+
|
|
7
29
|
## 1.0.0
|
|
8
30
|
|
|
9
31
|
Initial public release — the Scoring layer.
|
|
10
32
|
|
|
11
33
|
### Added
|
|
34
|
+
|
|
12
35
|
- `getCompleteness` (how well-documented a record is) and `score` /
|
|
13
36
|
`rankByCompellingness` (how anomalous a case is).
|
|
14
37
|
- Scoring over arrays of competing claims: results include a `range` (spread across
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ orthogonal measures:
|
|
|
13
13
|
|
|
14
14
|
## The DisclosureOS model
|
|
15
15
|
|
|
16
|
-
DisclosureOS is a **
|
|
16
|
+
DisclosureOS is a **six-part standard**. Each part answers one question about an
|
|
17
17
|
observation; `@disclosureos/schema` binds the package-owned pieces into one portable
|
|
18
18
|
contract (a single TS type + JSON Schema + non-stripping parse).
|
|
19
19
|
|
|
@@ -24,6 +24,7 @@ contract (a single TS type + JSON Schema + non-stripping parse).
|
|
|
24
24
|
| **Origins** | `@disclosureos/origins` | *What might explain it?* |
|
|
25
25
|
| **Claims** | `@disclosureos/records/shared` | *Who assessed it, why, and on what evidence?* |
|
|
26
26
|
| **Scoring** | `@disclosureos/scoring` | *How complete / compelling is the case?* |
|
|
27
|
+
| **Instruments** | `@disclosureos/instruments` | *What hardware produced the data?* |
|
|
27
28
|
|
|
28
29
|
Supporting packages: **`@disclosureos/schema`** (the portable contract that composes the
|
|
29
30
|
package-owned pieces), **`@disclosureos/cli`** (tooling), and **`@disclosureos/examples`**
|
|
@@ -33,8 +34,9 @@ package-owned pieces), **`@disclosureos/cli`** (tooling), and **`@disclosureos/e
|
|
|
33
34
|
|
|
34
35
|
Unlike `observables` and `origins`, scoring **only consumes** the foundation — it
|
|
35
36
|
reads `observableAssessments` and `origin` but adds no slot to `Observation`. It
|
|
36
|
-
depends on `@disclosureos/records`, `@disclosureos/observables`,
|
|
37
|
-
`@disclosureos/origins`, so importing it gives
|
|
37
|
+
depends on `@disclosureos/records`, `@disclosureos/observables`,
|
|
38
|
+
`@disclosureos/origins`, and `@disclosureos/instruments`, so importing it gives
|
|
39
|
+
you those slots typed automatically.
|
|
38
40
|
|
|
39
41
|
> **See it end to end.** [`examples/golden-path.ts`](../../examples/golden-path.ts)
|
|
40
42
|
> takes one observation through every layer — records → observables → origins → scoring,
|
|
@@ -68,6 +70,28 @@ Because evaluative slots hold **arrays of competing claims**, `score()` reports
|
|
|
68
70
|
`range` (the spread across claims) and a `contested` flag when evaluators disagree —
|
|
69
71
|
not just a single point estimate.
|
|
70
72
|
|
|
73
|
+
## Instrument trust (calibration provenance)
|
|
74
|
+
|
|
75
|
+
`calibrationTrust` turns published sensor manifests
|
|
76
|
+
([`@disclosureos/instruments`](../disclosureos-instruments)) into a scoring
|
|
77
|
+
input. It builds an `evaluatorWeight` policy that credits claims whose sensor
|
|
78
|
+
evidence resolves — via `SensorReading.sensorRef` — to a manifest entry with
|
|
79
|
+
real calibration provenance (`in_practice` / `documented` earn the most; see
|
|
80
|
+
`CALIBRATION_TRUST_WEIGHTS`).
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { score, calibrationTrust } from '@disclosureos/scoring';
|
|
84
|
+
|
|
85
|
+
const result = score(observation, {
|
|
86
|
+
evaluatorWeight: calibrationTrust(observation, [navyManifest]),
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The default gradient is deliberately conservative: unresolved sensors keep the
|
|
91
|
+
1.0 baseline (records that predate manifests are never penalized), and like
|
|
92
|
+
every `evaluatorWeight` policy it shifts only the consensus point — `range` and
|
|
93
|
+
`contested` still report the honest spread of what evaluators asserted.
|
|
94
|
+
|
|
71
95
|
## Subpath exports
|
|
72
96
|
|
|
73
97
|
| Subpath | Contents |
|
|
@@ -1,6 +1,56 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { Observation } from '@disclosureos/records';
|
|
3
3
|
import { AssessmentLevel } from '@disclosureos/observables';
|
|
4
|
+
import { CalibrationStatus, SensorManifest } from '@disclosureos/instruments';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Trust multiplier per calibration maturity level, plus the `unresolved`
|
|
8
|
+
* baseline for claims whose sensor evidence does not resolve to a manifest
|
|
9
|
+
* entry (no `sensorRef`, unknown org, unknown sensor, or no calibration block).
|
|
10
|
+
*/
|
|
11
|
+
type CalibrationTrustWeights = Record<CalibrationStatus | 'unresolved', number>;
|
|
12
|
+
/**
|
|
13
|
+
* Conservative default gradient: instruments with real calibration provenance
|
|
14
|
+
* earn *credit* over unresolved sensors — nothing is ever penalized below the
|
|
15
|
+
* baseline, so adopting the hook can only reward organizations that publish
|
|
16
|
+
* manifests, never punish records that predate them.
|
|
17
|
+
*
|
|
18
|
+
* @experimental The shape is stable; these specific values are a reference
|
|
19
|
+
* policy that may be re-tuned in a minor release. Pin your own `weights` via
|
|
20
|
+
* {@link CalibrationTrustOptions} if you need stable numbers.
|
|
21
|
+
*/
|
|
22
|
+
declare const CALIBRATION_TRUST_WEIGHTS: CalibrationTrustWeights;
|
|
23
|
+
interface CalibrationTrustOptions {
|
|
24
|
+
/** Override the trust gradient (defaults to {@link CALIBRATION_TRUST_WEIGHTS}). */
|
|
25
|
+
weights?: CalibrationTrustWeights;
|
|
26
|
+
/**
|
|
27
|
+
* A base trust policy composed (multiplied) with the calibration credit —
|
|
28
|
+
* e.g. an evaluator-identity policy. Defaults to uniform 1.0.
|
|
29
|
+
*/
|
|
30
|
+
evaluatorWeight?: (claim: EvaluatorWeightInput) => number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build an `evaluatorWeight` hook (see `ScoreOptions.evaluatorWeight`) that
|
|
34
|
+
* credits claims backed by instruments with published calibration provenance.
|
|
35
|
+
*
|
|
36
|
+
* Resolution chain, per claim: `evidenceRefs` (`"sensor:<id>"`) → the
|
|
37
|
+
* observation's `sensorEvidence.sensors[]` reading → its `sensorRef`
|
|
38
|
+
* (`"<org-slug>:<sensor-id>"`) → the matching manifest entry's
|
|
39
|
+
* `calibration.status`. When a claim cites several sensor readings, the
|
|
40
|
+
* strongest instrument backing wins. Claims with no resolvable sensor
|
|
41
|
+
* evidence get the `unresolved` baseline.
|
|
42
|
+
*
|
|
43
|
+
* Like every `evaluatorWeight` policy, this shifts only the consensus point
|
|
44
|
+
* estimate — `range` and `contested` still report the honest spread of what
|
|
45
|
+
* evaluators actually asserted.
|
|
46
|
+
*
|
|
47
|
+
* ```ts
|
|
48
|
+
* const result = score(observation, {
|
|
49
|
+
* evaluatorWeight: calibrationTrust(observation, [navyManifest]),
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
declare function calibrationTrust(observation: Observation, manifests: readonly SensorManifest[], options?: CalibrationTrustOptions): (claim: EvaluatorWeightInput) => number;
|
|
4
54
|
|
|
5
55
|
/** Methodology version stamped on every {@link ScoreResult}. Bump on weight/formula changes. */
|
|
6
56
|
declare const SCORING_VERSION: "2.0.0";
|
|
@@ -96,4 +146,4 @@ interface RankedObservation<T extends Observation = Observation> {
|
|
|
96
146
|
*/
|
|
97
147
|
declare function rankByCompellingness<T extends Observation>(observations: readonly T[], options?: ScoreOptions): RankedObservation<T>[];
|
|
98
148
|
|
|
99
|
-
export { ASSESSMENT_LEVEL_WEIGHTS, type CompellingnessWeights, CompellingnessWeightsSchema, DEFAULT_PROSAIC_OCS_PREFIXES, DEFAULT_WEIGHTS, type EvaluatorWeightInput, type RankedObservation, SCORING_VERSION, type ScoreOptions, type ScoreResult, ScoreResultSchema, rankByCompellingness, score };
|
|
149
|
+
export { ASSESSMENT_LEVEL_WEIGHTS, CALIBRATION_TRUST_WEIGHTS, type CalibrationTrustOptions, type CalibrationTrustWeights, type CompellingnessWeights, CompellingnessWeightsSchema, DEFAULT_PROSAIC_OCS_PREFIXES, DEFAULT_WEIGHTS, type EvaluatorWeightInput, type RankedObservation, SCORING_VERSION, type ScoreOptions, type ScoreResult, ScoreResultSchema, calibrationTrust, rankByCompellingness, score };
|
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import '@disclosureos/observables';
|
|
3
3
|
import '@disclosureos/origins';
|
|
4
|
+
import { parseSensorRef, parseEvidenceRef } from '@disclosureos/records/shared';
|
|
5
|
+
|
|
6
|
+
// src/compellingness/index.ts
|
|
7
|
+
var CALIBRATION_TRUST_WEIGHTS = {
|
|
8
|
+
unresolved: 1,
|
|
9
|
+
none: 1,
|
|
10
|
+
candidate_identified: 1.05,
|
|
11
|
+
in_practice: 1.15,
|
|
12
|
+
documented: 1.25
|
|
13
|
+
};
|
|
14
|
+
function calibrationTrust(observation, manifests, options) {
|
|
15
|
+
const weights = options?.weights ?? CALIBRATION_TRUST_WEIGHTS;
|
|
16
|
+
const base = options?.evaluatorWeight ?? (() => 1);
|
|
17
|
+
const statusBySensorId = /* @__PURE__ */ new Map();
|
|
18
|
+
for (const reading of observation.sensorEvidence?.sensors ?? []) {
|
|
19
|
+
if (!reading.sensorRef) continue;
|
|
20
|
+
const ref = parseSensorRef(reading.sensorRef);
|
|
21
|
+
if (!ref) continue;
|
|
22
|
+
const entry = manifests.find((manifest) => manifest.orgSlug === ref.orgSlug)?.sensors.find((sensor) => sensor.id === ref.sensorId);
|
|
23
|
+
const status = entry?.calibration?.status;
|
|
24
|
+
if (status) statusBySensorId.set(reading.id, status);
|
|
25
|
+
}
|
|
26
|
+
return (claim) => {
|
|
27
|
+
let credit = weights.unresolved;
|
|
28
|
+
for (const evidence of claim.evidenceRefs ?? []) {
|
|
29
|
+
const parsed = parseEvidenceRef(evidence);
|
|
30
|
+
if (!parsed || parsed.kind !== "sensor") continue;
|
|
31
|
+
const status = statusBySensorId.get(parsed.id);
|
|
32
|
+
const weight = status === void 0 ? weights.unresolved : weights[status];
|
|
33
|
+
if (weight > credit) credit = weight;
|
|
34
|
+
}
|
|
35
|
+
return base(claim) * credit;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
4
38
|
|
|
5
39
|
// src/compellingness/index.ts
|
|
6
40
|
var SCORING_VERSION = "2.0.0";
|
|
@@ -131,6 +165,6 @@ function rankByCompellingness(observations, options) {
|
|
|
131
165
|
return observations.map((observation) => ({ observation, result: score(observation, options) })).sort((a, b) => b.result.score - a.result.score);
|
|
132
166
|
}
|
|
133
167
|
|
|
134
|
-
export { ASSESSMENT_LEVEL_WEIGHTS, CompellingnessWeightsSchema, DEFAULT_PROSAIC_OCS_PREFIXES, DEFAULT_WEIGHTS, SCORING_VERSION, ScoreResultSchema, rankByCompellingness, score };
|
|
168
|
+
export { ASSESSMENT_LEVEL_WEIGHTS, CALIBRATION_TRUST_WEIGHTS, CompellingnessWeightsSchema, DEFAULT_PROSAIC_OCS_PREFIXES, DEFAULT_WEIGHTS, SCORING_VERSION, ScoreResultSchema, calibrationTrust, rankByCompellingness, score };
|
|
135
169
|
//# sourceMappingURL=index.js.map
|
|
136
170
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/compellingness/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\nimport type { Observation } from '@disclosureos/records';\nimport type { AssessmentLevel, ObservableClaim } from '@disclosureos/observables';\nimport type { OriginClaim } from '@disclosureos/origins';\n// Side-effect imports register the `observableAssessments` and `origin` slots on\n// `Observation` via module augmentation, so they are typed below.\nimport '@disclosureos/observables';\nimport '@disclosureos/origins';\n\n/** Methodology version stamped on every {@link ScoreResult}. Bump on weight/formula changes. */\nexport const SCORING_VERSION = '2.0.0' as const;\n\n/**\n * Ordinal anomaly strength per evidentiary tier. A `confirmed` signal counts\n * fully; `not_indicated` contributes nothing. Multiplied by the assessment's\n * confidence (default 1 when unstated). Exported so consumers can render the\n * methodology; override semantics may come in a future minor release.\n */\nexport const ASSESSMENT_LEVEL_WEIGHTS: Record<AssessmentLevel, number> = {\n not_indicated: 0,\n reported: 0.25,\n documented: 0.5,\n measured: 0.75,\n confirmed: 1,\n};\n\n/**\n * OCS id prefixes treated as prosaic (well-explained). A confident *prosaic*\n * classification means the case is explained, so it contributes no anomaly\n * signal. Overridable per {@link score} call. Default is the OCS \"Prosaic\"\n * branch (`1.1.1`).\n */\nexport const DEFAULT_PROSAIC_OCS_PREFIXES: readonly string[] = ['1.1.1'];\n\nexport const CompellingnessWeightsSchema = z\n .object({\n technology: z.number().min(0).describe('Weight of the technology-anomaly signal.'),\n biologics: z.number().min(0).describe('Weight of the biologics-anomaly signal.'),\n origin: z.number().min(0).describe('Weight of the (non-prosaic) origin-confidence signal.'),\n })\n .meta({ id: 'CompellingnessWeights' })\n .describe('Relative weights for the compellingness components. Need not sum to 1 — normalized internally.');\n\nexport type CompellingnessWeights = z.infer<typeof CompellingnessWeightsSchema>;\n\n/**\n * Fixed default weights, exposed as a transparent, overridable data object (not a\n * config system). Partners can propose alternate weight sets without an API change.\n *\n * @experimental The *shape* (`CompellingnessWeights`) is stable, but these specific\n * default values are a reference methodology that may be re-tuned in a minor release.\n * Pin your own `weights` via {@link ScoreOptions} if you need stable numbers.\n */\nexport const DEFAULT_WEIGHTS: CompellingnessWeights = {\n technology: 0.45,\n biologics: 0.35,\n origin: 0.2,\n};\n\nexport const ScoreResultSchema = z\n .object({\n score: z.number().min(0).max(1).describe('Overall compellingness (consensus point estimate), 0–1.'),\n range: z\n .object({\n low: z.number().min(0).max(1).describe('Most-skeptical reading of the strongest-anomaly signal across competing claims.'),\n high: z.number().min(0).max(1).describe('Most-anomalous reading of the strongest-anomaly signal across competing claims.'),\n })\n .describe(\n 'Spread of the score across competing claims — wide range signals disagreement. Bounds are '\n + 'computed per-signal under the same strongest-anomaly (max) reduction as the point estimate, '\n + 'not a global min/max over every claim.',\n ),\n contested: z\n .boolean()\n .describe('True when claims disagree on *direction* (some assert anomaly, some assert none).'),\n components: z\n .object({\n technology: z.number().min(0).max(1),\n biologics: z.number().min(0).max(1),\n origin: z.number().min(0).max(1),\n })\n .describe('Per-signal consensus contributions before weighting.'),\n weights: CompellingnessWeightsSchema,\n scoringVersion: z.string().describe('Methodology version that produced this score.'),\n })\n .meta({ id: 'ScoreResult' })\n .describe('A reference compellingness score for an observation, with a contestedness range.');\n\nexport type ScoreResult = z.infer<typeof ScoreResultSchema>;\n\n/** The minimal claim shape the trust hook sees (it never needs the full body). */\nexport type EvaluatorWeightInput = {\n evaluatedBy?: string | undefined;\n evidenceRefs?: string[] | undefined;\n};\n\nexport interface ScoreOptions {\n weights?: CompellingnessWeights;\n /** Override the prosaic OCS prefixes (defaults to {@link DEFAULT_PROSAIC_OCS_PREFIXES}). */\n prosaicPrefixes?: readonly string[];\n /**\n * Trust applied to each claim's vote in the consensus point estimate (default\n * 1.0 — uniform). This is where a future trust policy (e.g.\n * weighting verified/official evaluators higher) plugs in here without changing\n * the formula. Range/contestedness are NOT affected — they report the honest\n * spread of what evaluators actually asserted.\n *\n * @experimental The hook signature is stable, but the default trust *policy*\n * (uniform 1.0) is deliberately unopinionated and is a separate, ongoing design\n * task; a future built-in policy may change the default.\n */\n evaluatorWeight?: (claim: EvaluatorWeightInput) => number;\n}\n\nconst UNIFORM_TRUST = (): number => 1;\n\nfunction isProsaic(id: string, prefixes: readonly string[]): boolean {\n return prefixes.some((p) => id === p || id.startsWith(`${p}.`));\n}\n\nfunction round(n: number): number {\n return Math.round(n * 1e4) / 1e4;\n}\n\ntype Weighted = { magnitude: number; weight: number };\n\ninterface ClaimStats {\n /** Trust-weighted mean magnitude (the consensus point). */\n point: number;\n /** Min asserted magnitude (most skeptical). */\n low: number;\n /** Max asserted magnitude (most anomalous). */\n high: number;\n /** Claims disagree on direction (some > 0, some === 0). */\n contested: boolean;\n}\n\nconst EMPTY_STATS: ClaimStats = { point: 0, low: 0, high: 0, contested: false };\n\nfunction reduceClaims(weighted: Weighted[]): ClaimStats {\n if (weighted.length === 0) return EMPTY_STATS;\n let wsum = 0;\n let msum = 0;\n let low = Infinity;\n let high = -Infinity;\n let anyAnomaly = false;\n let anyNone = false;\n for (const { magnitude, weight } of weighted) {\n wsum += weight;\n msum += magnitude * weight;\n if (magnitude < low) low = magnitude;\n if (magnitude > high) high = magnitude;\n if (magnitude > 0) anyAnomaly = true;\n else anyNone = true;\n }\n return {\n point: wsum > 0 ? msum / wsum : 0,\n low,\n high,\n contested: anyAnomaly && anyNone,\n };\n}\n\n/** Asserted anomaly magnitude of one observable claim, in [0,1]. */\nfunction observableMagnitude(claim: ObservableClaim): number {\n return ASSESSMENT_LEVEL_WEIGHTS[claim.level] * (claim.confidence ?? 1);\n}\n\n/**\n * Strongest established anomaly within a domain — the \"is there *any* anomaly?\" reduction.\n * `point`/`low`/`high` are each a **max across observables** of that observable's claim-list\n * stat. So `low` is \"the most-skeptical reading of the strongest-anomaly observable,\" not a\n * global floor across all observables — consistent with the point estimate's max reduction.\n */\nfunction domainStats(\n group: Partial<Record<string, ObservableClaim[]>> | undefined,\n evaluatorWeight: (claim: EvaluatorWeightInput) => number,\n): ClaimStats {\n if (!group) return EMPTY_STATS;\n let point = 0;\n let low = 0;\n let high = 0;\n let contested = false;\n for (const claims of Object.values(group)) {\n if (!claims || claims.length === 0) continue;\n const weighted = claims.map<Weighted>((c) => ({\n magnitude: observableMagnitude(c),\n weight: (c.confidence ?? 1) * evaluatorWeight(c),\n }));\n const stats = reduceClaims(weighted);\n if (stats.point > point) point = stats.point;\n if (stats.low > low) low = stats.low;\n if (stats.high > high) high = stats.high;\n if (stats.contested) contested = true;\n }\n return { point, low, high, contested };\n}\n\n/** Confidence in a *non-prosaic* origin, reduced across competing classifications. */\nfunction originStats(\n origin: OriginClaim[] | undefined,\n prefixes: readonly string[],\n evaluatorWeight: (claim: EvaluatorWeightInput) => number,\n): ClaimStats {\n if (!origin || origin.length === 0) return EMPTY_STATS;\n const weighted = origin.map<Weighted>((c) => ({\n magnitude: isProsaic(c.primaryHypothesis, prefixes) ? 0 : c.confidence,\n weight: c.confidence * evaluatorWeight(c),\n }));\n return reduceClaims(weighted);\n}\n\n/**\n * Reference compellingness scorer. Combines the strongest technology- and\n * biologics-anomaly signals (from `observableAssessments`) with the confidence in\n * a *non-prosaic* origin classification (from `origin`), under transparent,\n * overridable weights. Each slot is a *list* of competing claims, so the scorer\n * reports a consensus `score`, the `range` of readings across claims, and whether\n * the case is `contested` (evaluators disagree on direction). This answers \"how\n * strongly does this case bear on the two public questions — anomalous\n * tech/biologics, and a non-mundane origin?\" — distinct from completeness.\n */\nexport function score(observation: Observation, options?: ScoreOptions): ScoreResult {\n const weights = options?.weights ?? DEFAULT_WEIGHTS;\n const prosaicPrefixes = options?.prosaicPrefixes ?? DEFAULT_PROSAIC_OCS_PREFIXES;\n const evaluatorWeight = options?.evaluatorWeight ?? UNIFORM_TRUST;\n\n const assessments = observation.observableAssessments;\n const technology = domainStats(assessments?.technology, evaluatorWeight);\n const biologics = domainStats(assessments?.biologics, evaluatorWeight);\n const origin = originStats(observation.origin, prosaicPrefixes, evaluatorWeight);\n\n const totalWeight = weights.technology + weights.biologics + weights.origin;\n const combine = (t: number, b: number, o: number): number =>\n totalWeight > 0\n ? (t * weights.technology + b * weights.biologics + o * weights.origin) / totalWeight\n : 0;\n\n return {\n score: round(combine(technology.point, biologics.point, origin.point)),\n range: {\n low: round(combine(technology.low, biologics.low, origin.low)),\n high: round(combine(technology.high, biologics.high, origin.high)),\n },\n contested: technology.contested || biologics.contested || origin.contested,\n components: {\n technology: round(technology.point),\n biologics: round(biologics.point),\n origin: round(origin.point),\n },\n weights,\n scoringVersion: SCORING_VERSION,\n };\n}\n\nexport interface RankedObservation<T extends Observation = Observation> {\n observation: T;\n result: ScoreResult;\n}\n\n/**\n * Score a set of observations and return them sorted most-compelling first — the\n * \"float the strongest cases to the top\" use case.\n */\nexport function rankByCompellingness<T extends Observation>(\n observations: readonly T[],\n options?: ScoreOptions,\n): RankedObservation<T>[] {\n return observations\n .map((observation) => ({ observation, result: score(observation, options) }))\n .sort((a, b) => b.result.score - a.result.score);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/compellingness/calibration.ts","../../src/compellingness/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Observation } from '@disclosureos/records';\nimport { parseEvidenceRef, parseSensorRef } from '@disclosureos/records/shared';\nimport type { CalibrationStatus, SensorManifest } from '@disclosureos/instruments';\nimport type { EvaluatorWeightInput } from './index';\n\n/**\n * Trust multiplier per calibration maturity level, plus the `unresolved`\n * baseline for claims whose sensor evidence does not resolve to a manifest\n * entry (no `sensorRef`, unknown org, unknown sensor, or no calibration block).\n */\nexport type CalibrationTrustWeights = Record<CalibrationStatus | 'unresolved', number>;\n\n/**\n * Conservative default gradient: instruments with real calibration provenance\n * earn *credit* over unresolved sensors — nothing is ever penalized below the\n * baseline, so adopting the hook can only reward organizations that publish\n * manifests, never punish records that predate them.\n *\n * @experimental The shape is stable; these specific values are a reference\n * policy that may be re-tuned in a minor release. Pin your own `weights` via\n * {@link CalibrationTrustOptions} if you need stable numbers.\n */\nexport const CALIBRATION_TRUST_WEIGHTS: CalibrationTrustWeights = {\n unresolved: 1,\n none: 1,\n candidate_identified: 1.05,\n in_practice: 1.15,\n documented: 1.25,\n};\n\nexport interface CalibrationTrustOptions {\n /** Override the trust gradient (defaults to {@link CALIBRATION_TRUST_WEIGHTS}). */\n weights?: CalibrationTrustWeights;\n /**\n * A base trust policy composed (multiplied) with the calibration credit —\n * e.g. an evaluator-identity policy. Defaults to uniform 1.0.\n */\n evaluatorWeight?: (claim: EvaluatorWeightInput) => number;\n}\n\n/**\n * Build an `evaluatorWeight` hook (see `ScoreOptions.evaluatorWeight`) that\n * credits claims backed by instruments with published calibration provenance.\n *\n * Resolution chain, per claim: `evidenceRefs` (`\"sensor:<id>\"`) → the\n * observation's `sensorEvidence.sensors[]` reading → its `sensorRef`\n * (`\"<org-slug>:<sensor-id>\"`) → the matching manifest entry's\n * `calibration.status`. When a claim cites several sensor readings, the\n * strongest instrument backing wins. Claims with no resolvable sensor\n * evidence get the `unresolved` baseline.\n *\n * Like every `evaluatorWeight` policy, this shifts only the consensus point\n * estimate — `range` and `contested` still report the honest spread of what\n * evaluators actually asserted.\n *\n * ```ts\n * const result = score(observation, {\n * evaluatorWeight: calibrationTrust(observation, [navyManifest]),\n * });\n * ```\n */\nexport function calibrationTrust(\n observation: Observation,\n manifests: readonly SensorManifest[],\n options?: CalibrationTrustOptions,\n): (claim: EvaluatorWeightInput) => number {\n const weights = options?.weights ?? CALIBRATION_TRUST_WEIGHTS;\n const base = options?.evaluatorWeight ?? ((): number => 1);\n\n // Pre-resolve each in-record sensor reading to its manifest calibration status.\n const statusBySensorId = new Map<string, CalibrationStatus>();\n for (const reading of observation.sensorEvidence?.sensors ?? []) {\n if (!reading.sensorRef) continue;\n const ref = parseSensorRef(reading.sensorRef);\n if (!ref) continue;\n const entry = manifests\n .find((manifest) => manifest.orgSlug === ref.orgSlug)\n ?.sensors.find((sensor) => sensor.id === ref.sensorId);\n const status = entry?.calibration?.status;\n if (status) statusBySensorId.set(reading.id, status);\n }\n\n return (claim) => {\n let credit = weights.unresolved;\n for (const evidence of claim.evidenceRefs ?? []) {\n const parsed = parseEvidenceRef(evidence);\n if (!parsed || parsed.kind !== 'sensor') continue;\n const status = statusBySensorId.get(parsed.id);\n const weight = status === undefined ? weights.unresolved : weights[status];\n if (weight > credit) credit = weight;\n }\n return base(claim) * credit;\n };\n}\n","import { z } from 'zod';\nimport type { Observation } from '@disclosureos/records';\nimport type { AssessmentLevel, ObservableClaim } from '@disclosureos/observables';\nimport type { OriginClaim } from '@disclosureos/origins';\n// Side-effect imports register the `observableAssessments` and `origin` slots on\n// `Observation` via module augmentation, so they are typed below.\nimport '@disclosureos/observables';\nimport '@disclosureos/origins';\n\nexport { calibrationTrust, CALIBRATION_TRUST_WEIGHTS } from './calibration';\nexport type { CalibrationTrustWeights, CalibrationTrustOptions } from './calibration';\n\n/** Methodology version stamped on every {@link ScoreResult}. Bump on weight/formula changes. */\nexport const SCORING_VERSION = '2.0.0' as const;\n\n/**\n * Ordinal anomaly strength per evidentiary tier. A `confirmed` signal counts\n * fully; `not_indicated` contributes nothing. Multiplied by the assessment's\n * confidence (default 1 when unstated). Exported so consumers can render the\n * methodology; override semantics may come in a future minor release.\n */\nexport const ASSESSMENT_LEVEL_WEIGHTS: Record<AssessmentLevel, number> = {\n not_indicated: 0,\n reported: 0.25,\n documented: 0.5,\n measured: 0.75,\n confirmed: 1,\n};\n\n/**\n * OCS id prefixes treated as prosaic (well-explained). A confident *prosaic*\n * classification means the case is explained, so it contributes no anomaly\n * signal. Overridable per {@link score} call. Default is the OCS \"Prosaic\"\n * branch (`1.1.1`).\n */\nexport const DEFAULT_PROSAIC_OCS_PREFIXES: readonly string[] = ['1.1.1'];\n\nexport const CompellingnessWeightsSchema = z\n .object({\n technology: z.number().min(0).describe('Weight of the technology-anomaly signal.'),\n biologics: z.number().min(0).describe('Weight of the biologics-anomaly signal.'),\n origin: z.number().min(0).describe('Weight of the (non-prosaic) origin-confidence signal.'),\n })\n .meta({ id: 'CompellingnessWeights' })\n .describe('Relative weights for the compellingness components. Need not sum to 1 — normalized internally.');\n\nexport type CompellingnessWeights = z.infer<typeof CompellingnessWeightsSchema>;\n\n/**\n * Fixed default weights, exposed as a transparent, overridable data object (not a\n * config system). Partners can propose alternate weight sets without an API change.\n *\n * @experimental The *shape* (`CompellingnessWeights`) is stable, but these specific\n * default values are a reference methodology that may be re-tuned in a minor release.\n * Pin your own `weights` via {@link ScoreOptions} if you need stable numbers.\n */\nexport const DEFAULT_WEIGHTS: CompellingnessWeights = {\n technology: 0.45,\n biologics: 0.35,\n origin: 0.2,\n};\n\nexport const ScoreResultSchema = z\n .object({\n score: z.number().min(0).max(1).describe('Overall compellingness (consensus point estimate), 0–1.'),\n range: z\n .object({\n low: z.number().min(0).max(1).describe('Most-skeptical reading of the strongest-anomaly signal across competing claims.'),\n high: z.number().min(0).max(1).describe('Most-anomalous reading of the strongest-anomaly signal across competing claims.'),\n })\n .describe(\n 'Spread of the score across competing claims — wide range signals disagreement. Bounds are '\n + 'computed per-signal under the same strongest-anomaly (max) reduction as the point estimate, '\n + 'not a global min/max over every claim.',\n ),\n contested: z\n .boolean()\n .describe('True when claims disagree on *direction* (some assert anomaly, some assert none).'),\n components: z\n .object({\n technology: z.number().min(0).max(1),\n biologics: z.number().min(0).max(1),\n origin: z.number().min(0).max(1),\n })\n .describe('Per-signal consensus contributions before weighting.'),\n weights: CompellingnessWeightsSchema,\n scoringVersion: z.string().describe('Methodology version that produced this score.'),\n })\n .meta({ id: 'ScoreResult' })\n .describe('A reference compellingness score for an observation, with a contestedness range.');\n\nexport type ScoreResult = z.infer<typeof ScoreResultSchema>;\n\n/** The minimal claim shape the trust hook sees (it never needs the full body). */\nexport type EvaluatorWeightInput = {\n evaluatedBy?: string | undefined;\n evidenceRefs?: string[] | undefined;\n};\n\nexport interface ScoreOptions {\n weights?: CompellingnessWeights;\n /** Override the prosaic OCS prefixes (defaults to {@link DEFAULT_PROSAIC_OCS_PREFIXES}). */\n prosaicPrefixes?: readonly string[];\n /**\n * Trust applied to each claim's vote in the consensus point estimate (default\n * 1.0 — uniform). This is where a future trust policy (e.g.\n * weighting verified/official evaluators higher) plugs in here without changing\n * the formula. Range/contestedness are NOT affected — they report the honest\n * spread of what evaluators actually asserted.\n *\n * @experimental The hook signature is stable, but the default trust *policy*\n * (uniform 1.0) is deliberately unopinionated and is a separate, ongoing design\n * task; a future built-in policy may change the default.\n */\n evaluatorWeight?: (claim: EvaluatorWeightInput) => number;\n}\n\nconst UNIFORM_TRUST = (): number => 1;\n\nfunction isProsaic(id: string, prefixes: readonly string[]): boolean {\n return prefixes.some((p) => id === p || id.startsWith(`${p}.`));\n}\n\nfunction round(n: number): number {\n return Math.round(n * 1e4) / 1e4;\n}\n\ntype Weighted = { magnitude: number; weight: number };\n\ninterface ClaimStats {\n /** Trust-weighted mean magnitude (the consensus point). */\n point: number;\n /** Min asserted magnitude (most skeptical). */\n low: number;\n /** Max asserted magnitude (most anomalous). */\n high: number;\n /** Claims disagree on direction (some > 0, some === 0). */\n contested: boolean;\n}\n\nconst EMPTY_STATS: ClaimStats = { point: 0, low: 0, high: 0, contested: false };\n\nfunction reduceClaims(weighted: Weighted[]): ClaimStats {\n if (weighted.length === 0) return EMPTY_STATS;\n let wsum = 0;\n let msum = 0;\n let low = Infinity;\n let high = -Infinity;\n let anyAnomaly = false;\n let anyNone = false;\n for (const { magnitude, weight } of weighted) {\n wsum += weight;\n msum += magnitude * weight;\n if (magnitude < low) low = magnitude;\n if (magnitude > high) high = magnitude;\n if (magnitude > 0) anyAnomaly = true;\n else anyNone = true;\n }\n return {\n point: wsum > 0 ? msum / wsum : 0,\n low,\n high,\n contested: anyAnomaly && anyNone,\n };\n}\n\n/** Asserted anomaly magnitude of one observable claim, in [0,1]. */\nfunction observableMagnitude(claim: ObservableClaim): number {\n return ASSESSMENT_LEVEL_WEIGHTS[claim.level] * (claim.confidence ?? 1);\n}\n\n/**\n * Strongest established anomaly within a domain — the \"is there *any* anomaly?\" reduction.\n * `point`/`low`/`high` are each a **max across observables** of that observable's claim-list\n * stat. So `low` is \"the most-skeptical reading of the strongest-anomaly observable,\" not a\n * global floor across all observables — consistent with the point estimate's max reduction.\n */\nfunction domainStats(\n group: Partial<Record<string, ObservableClaim[]>> | undefined,\n evaluatorWeight: (claim: EvaluatorWeightInput) => number,\n): ClaimStats {\n if (!group) return EMPTY_STATS;\n let point = 0;\n let low = 0;\n let high = 0;\n let contested = false;\n for (const claims of Object.values(group)) {\n if (!claims || claims.length === 0) continue;\n const weighted = claims.map<Weighted>((c) => ({\n magnitude: observableMagnitude(c),\n weight: (c.confidence ?? 1) * evaluatorWeight(c),\n }));\n const stats = reduceClaims(weighted);\n if (stats.point > point) point = stats.point;\n if (stats.low > low) low = stats.low;\n if (stats.high > high) high = stats.high;\n if (stats.contested) contested = true;\n }\n return { point, low, high, contested };\n}\n\n/** Confidence in a *non-prosaic* origin, reduced across competing classifications. */\nfunction originStats(\n origin: OriginClaim[] | undefined,\n prefixes: readonly string[],\n evaluatorWeight: (claim: EvaluatorWeightInput) => number,\n): ClaimStats {\n if (!origin || origin.length === 0) return EMPTY_STATS;\n const weighted = origin.map<Weighted>((c) => ({\n magnitude: isProsaic(c.primaryHypothesis, prefixes) ? 0 : c.confidence,\n weight: c.confidence * evaluatorWeight(c),\n }));\n return reduceClaims(weighted);\n}\n\n/**\n * Reference compellingness scorer. Combines the strongest technology- and\n * biologics-anomaly signals (from `observableAssessments`) with the confidence in\n * a *non-prosaic* origin classification (from `origin`), under transparent,\n * overridable weights. Each slot is a *list* of competing claims, so the scorer\n * reports a consensus `score`, the `range` of readings across claims, and whether\n * the case is `contested` (evaluators disagree on direction). This answers \"how\n * strongly does this case bear on the two public questions — anomalous\n * tech/biologics, and a non-mundane origin?\" — distinct from completeness.\n */\nexport function score(observation: Observation, options?: ScoreOptions): ScoreResult {\n const weights = options?.weights ?? DEFAULT_WEIGHTS;\n const prosaicPrefixes = options?.prosaicPrefixes ?? DEFAULT_PROSAIC_OCS_PREFIXES;\n const evaluatorWeight = options?.evaluatorWeight ?? UNIFORM_TRUST;\n\n const assessments = observation.observableAssessments;\n const technology = domainStats(assessments?.technology, evaluatorWeight);\n const biologics = domainStats(assessments?.biologics, evaluatorWeight);\n const origin = originStats(observation.origin, prosaicPrefixes, evaluatorWeight);\n\n const totalWeight = weights.technology + weights.biologics + weights.origin;\n const combine = (t: number, b: number, o: number): number =>\n totalWeight > 0\n ? (t * weights.technology + b * weights.biologics + o * weights.origin) / totalWeight\n : 0;\n\n return {\n score: round(combine(technology.point, biologics.point, origin.point)),\n range: {\n low: round(combine(technology.low, biologics.low, origin.low)),\n high: round(combine(technology.high, biologics.high, origin.high)),\n },\n contested: technology.contested || biologics.contested || origin.contested,\n components: {\n technology: round(technology.point),\n biologics: round(biologics.point),\n origin: round(origin.point),\n },\n weights,\n scoringVersion: SCORING_VERSION,\n };\n}\n\nexport interface RankedObservation<T extends Observation = Observation> {\n observation: T;\n result: ScoreResult;\n}\n\n/**\n * Score a set of observations and return them sorted most-compelling first — the\n * \"float the strongest cases to the top\" use case.\n */\nexport function rankByCompellingness<T extends Observation>(\n observations: readonly T[],\n options?: ScoreOptions,\n): RankedObservation<T>[] {\n return observations\n .map((observation) => ({ observation, result: score(observation, options) }))\n .sort((a, b) => b.result.score - a.result.score);\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { CompletenessOptions, CompletenessResult, CompletenessResultSchema, FieldPath, deriveFieldPaths, getCompleteness } from './completeness/index.js';
|
|
2
|
-
export { ASSESSMENT_LEVEL_WEIGHTS, CompellingnessWeights, CompellingnessWeightsSchema, DEFAULT_PROSAIC_OCS_PREFIXES, DEFAULT_WEIGHTS, EvaluatorWeightInput, RankedObservation, SCORING_VERSION, ScoreOptions, ScoreResult, ScoreResultSchema, rankByCompellingness, score } from './compellingness/index.js';
|
|
2
|
+
export { ASSESSMENT_LEVEL_WEIGHTS, CALIBRATION_TRUST_WEIGHTS, CalibrationTrustOptions, CalibrationTrustWeights, CompellingnessWeights, CompellingnessWeightsSchema, DEFAULT_PROSAIC_OCS_PREFIXES, DEFAULT_WEIGHTS, EvaluatorWeightInput, RankedObservation, SCORING_VERSION, ScoreOptions, ScoreResult, ScoreResultSchema, calibrationTrust, rankByCompellingness, score } from './compellingness/index.js';
|
|
3
3
|
import 'zod';
|
|
4
4
|
import '@disclosureos/records';
|
|
5
5
|
import '@disclosureos/observables';
|
|
6
|
+
import '@disclosureos/instruments';
|
|
6
7
|
|
|
7
8
|
declare const SCORING_SCHEMA_VERSION = "2.0.0";
|
|
8
9
|
declare const SCORING_SCHEMA_ID: string;
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { recordsJsonSchema } from '@disclosureos/records';
|
|
3
3
|
import '@disclosureos/observables';
|
|
4
4
|
import '@disclosureos/origins';
|
|
5
|
-
import { schemaId, jsonSchemaEnvelope } from '@disclosureos/records/shared';
|
|
5
|
+
import { schemaId, parseSensorRef, parseEvidenceRef, jsonSchemaEnvelope } from '@disclosureos/records/shared';
|
|
6
6
|
|
|
7
7
|
// src/completeness/index.ts
|
|
8
8
|
function refName(ref) {
|
|
@@ -106,6 +106,39 @@ function getCompleteness(observation, options) {
|
|
|
106
106
|
requiredPercentage: pct(requiredPresent, requiredTotal)
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
|
+
var CALIBRATION_TRUST_WEIGHTS = {
|
|
110
|
+
unresolved: 1,
|
|
111
|
+
none: 1,
|
|
112
|
+
candidate_identified: 1.05,
|
|
113
|
+
in_practice: 1.15,
|
|
114
|
+
documented: 1.25
|
|
115
|
+
};
|
|
116
|
+
function calibrationTrust(observation, manifests, options) {
|
|
117
|
+
const weights = options?.weights ?? CALIBRATION_TRUST_WEIGHTS;
|
|
118
|
+
const base = options?.evaluatorWeight ?? (() => 1);
|
|
119
|
+
const statusBySensorId = /* @__PURE__ */ new Map();
|
|
120
|
+
for (const reading of observation.sensorEvidence?.sensors ?? []) {
|
|
121
|
+
if (!reading.sensorRef) continue;
|
|
122
|
+
const ref = parseSensorRef(reading.sensorRef);
|
|
123
|
+
if (!ref) continue;
|
|
124
|
+
const entry = manifests.find((manifest) => manifest.orgSlug === ref.orgSlug)?.sensors.find((sensor) => sensor.id === ref.sensorId);
|
|
125
|
+
const status = entry?.calibration?.status;
|
|
126
|
+
if (status) statusBySensorId.set(reading.id, status);
|
|
127
|
+
}
|
|
128
|
+
return (claim) => {
|
|
129
|
+
let credit = weights.unresolved;
|
|
130
|
+
for (const evidence of claim.evidenceRefs ?? []) {
|
|
131
|
+
const parsed = parseEvidenceRef(evidence);
|
|
132
|
+
if (!parsed || parsed.kind !== "sensor") continue;
|
|
133
|
+
const status = statusBySensorId.get(parsed.id);
|
|
134
|
+
const weight = status === void 0 ? weights.unresolved : weights[status];
|
|
135
|
+
if (weight > credit) credit = weight;
|
|
136
|
+
}
|
|
137
|
+
return base(claim) * credit;
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/compellingness/index.ts
|
|
109
142
|
var SCORING_VERSION = "2.0.0";
|
|
110
143
|
var ASSESSMENT_LEVEL_WEIGHTS = {
|
|
111
144
|
not_indicated: 0,
|
|
@@ -247,6 +280,6 @@ function scoringJsonSchema() {
|
|
|
247
280
|
return jsonSchemaEnvelope(bundle, { id: SCORING_SCHEMA_ID, version: SCORING_SCHEMA_VERSION });
|
|
248
281
|
}
|
|
249
282
|
|
|
250
|
-
export { ASSESSMENT_LEVEL_WEIGHTS, CompellingnessWeightsSchema, CompletenessResultSchema, DEFAULT_PROSAIC_OCS_PREFIXES, DEFAULT_WEIGHTS, SCORING_SCHEMA_ID, SCORING_SCHEMA_VERSION, SCORING_VERSION, ScoreResultSchema, deriveFieldPaths, getCompleteness, rankByCompellingness, score, scoringJsonSchema };
|
|
283
|
+
export { ASSESSMENT_LEVEL_WEIGHTS, CALIBRATION_TRUST_WEIGHTS, CompellingnessWeightsSchema, CompletenessResultSchema, DEFAULT_PROSAIC_OCS_PREFIXES, DEFAULT_WEIGHTS, SCORING_SCHEMA_ID, SCORING_SCHEMA_VERSION, SCORING_VERSION, ScoreResultSchema, calibrationTrust, deriveFieldPaths, getCompleteness, rankByCompellingness, score, scoringJsonSchema };
|
|
251
284
|
//# sourceMappingURL=index.js.map
|
|
252
285
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/completeness/field-paths.ts","../src/completeness/index.ts","../src/compellingness/index.ts","../src/schema.ts"],"names":["z"],"mappings":"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","file":"index.js","sourcesContent":["import { recordsJsonSchema } from '@disclosureos/records';\n\n/** A leaf field path derived from the records JSON Schema. */\nexport interface FieldPath {\n /** Dot-path into an `Observation` (e.g. `location.latitude`). */\n path: string;\n /** True only when every segment along the path is schema-required. */\n required: boolean;\n}\n\ninterface SchemaNode {\n $ref?: string;\n properties?: Record<string, SchemaNode>;\n required?: string[];\n type?: string | string[];\n}\n\nfunction refName(ref: string): string {\n return ref.replace('#/$defs/', '');\n}\n\n/**\n * Walk the committed records JSON Schema and enumerate every leaf field path on\n * `Observation`. Objects are recursed (resolving `$ref` into `$defs`); arrays,\n * enums, scalars, and `record`/`anyOf` nodes are treated as leaves. This is the\n * single source of completeness coverage — there is no hand-maintained path list\n * (so adding a field to the schema automatically extends completeness).\n */\nexport function deriveFieldPaths(schema?: Record<string, unknown>): FieldPath[] {\n const root = (schema ?? recordsJsonSchema()) as Record<string, unknown> & {\n $ref?: string;\n $defs?: Record<string, SchemaNode>;\n };\n const defs = root.$defs ?? {};\n const out: FieldPath[] = [];\n\n function resolve(node: SchemaNode): SchemaNode {\n let cur: SchemaNode = node;\n const seen = new Set<string>();\n while (cur.$ref) {\n const name = refName(cur.$ref);\n if (seen.has(name)) break;\n seen.add(name);\n cur = defs[name] ?? {};\n }\n return cur;\n }\n\n function walk(node: SchemaNode, prefix: string, required: boolean, visited: Set<string>): void {\n const refn = node.$ref ? refName(node.$ref) : undefined;\n // Cycle guard: a recursive $ref re-entering an in-progress def becomes a leaf.\n if (refn && visited.has(refn)) {\n if (prefix) out.push({ path: prefix, required });\n return;\n }\n const resolved = resolve(node);\n const props = resolved.properties;\n if (props && Object.keys(props).length > 0) {\n const nextVisited = refn ? new Set([...visited, refn]) : visited;\n const req = new Set(resolved.required ?? []);\n for (const [key, child] of Object.entries(props)) {\n const childPath = prefix ? `${prefix}.${key}` : key;\n walk(child, childPath, required && req.has(key), nextVisited);\n }\n return;\n }\n if (prefix) out.push({ path: prefix, required });\n }\n\n if (root.$ref) {\n walk({ $ref: root.$ref }, '', true, new Set());\n }\n return out;\n}\n","import { z } from 'zod';\nimport type { Observation } from '@disclosureos/records';\nimport { deriveFieldPaths } from './field-paths';\nimport type { FieldPath } from './field-paths';\n\nexport type { FieldPath } from './field-paths';\nexport { deriveFieldPaths } from './field-paths';\n\nexport const CompletenessResultSchema = z\n .object({\n total: z.number().int().describe('Total leaf field paths defined by the records schema.'),\n present: z.number().int().describe('How many of those paths carry a value.'),\n percentage: z.number().int().describe('present / total, as a 0–100 integer.'),\n missing: z.array(z.string()).describe('Paths that are absent or empty.'),\n requiredTotal: z.number().int().describe('Count of schema-required leaf paths.'),\n requiredPresent: z.number().int().describe('How many required paths carry a value.'),\n requiredPercentage: z\n .number()\n .int()\n .describe('requiredPresent / requiredTotal, as a 0–100 integer.'),\n })\n .meta({ id: 'CompletenessResult' })\n .describe('How fully an observation populates the records schema.');\n\nexport type CompletenessResult = z.infer<typeof CompletenessResultSchema>;\n\nexport interface CompletenessOptions {\n /** Override the derived field-path set (defaults to the records schema). */\n paths?: FieldPath[];\n}\n\nfunction getNested(obj: Record<string, unknown>, path: string): unknown {\n let current: unknown = obj;\n for (const part of path.split('.')) {\n if (current == null || typeof current !== 'object') return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\nfunction isPresent(value: unknown): boolean {\n if (value == null) return false;\n if (typeof value === 'string') return value.length > 0;\n if (Array.isArray(value)) return value.length > 0;\n if (typeof value === 'object') return Object.keys(value).length > 0;\n return true;\n}\n\n// Field paths are stable for a given schema version, so derive once and reuse.\nlet cachedPaths: FieldPath[] | null = null;\nfunction defaultPaths(): FieldPath[] {\n return (cachedPaths ??= deriveFieldPaths());\n}\n\nfunction pct(present: number, total: number): number {\n return total > 0 ? Math.round((present / total) * 100) : 0;\n}\n\n/**\n * Measure how fully an observation populates the records schema. This is the\n * \"is it well-documented?\" axis — deliberately kept separate from compellingness\n * (\"is it anomalous?\"), so a thin-but-anomalous case never masquerades as complete.\n */\nexport function getCompleteness(\n observation: Observation | Record<string, unknown>,\n options?: CompletenessOptions,\n): CompletenessResult {\n const record = observation as Record<string, unknown>;\n const paths = options?.paths ?? defaultPaths();\n const missing: string[] = [];\n let present = 0;\n let requiredTotal = 0;\n let requiredPresent = 0;\n\n for (const field of paths) {\n const ok = isPresent(getNested(record, field.path));\n if (ok) present++;\n else missing.push(field.path);\n if (field.required) {\n requiredTotal++;\n if (ok) requiredPresent++;\n }\n }\n\n return {\n total: paths.length,\n present,\n percentage: pct(present, paths.length),\n missing,\n requiredTotal,\n requiredPresent,\n requiredPercentage: pct(requiredPresent, requiredTotal),\n };\n}\n","import { z } from 'zod';\nimport type { Observation } from '@disclosureos/records';\nimport type { AssessmentLevel, ObservableClaim } from '@disclosureos/observables';\nimport type { OriginClaim } from '@disclosureos/origins';\n// Side-effect imports register the `observableAssessments` and `origin` slots on\n// `Observation` via module augmentation, so they are typed below.\nimport '@disclosureos/observables';\nimport '@disclosureos/origins';\n\n/** Methodology version stamped on every {@link ScoreResult}. Bump on weight/formula changes. */\nexport const SCORING_VERSION = '2.0.0' as const;\n\n/**\n * Ordinal anomaly strength per evidentiary tier. A `confirmed` signal counts\n * fully; `not_indicated` contributes nothing. Multiplied by the assessment's\n * confidence (default 1 when unstated). Exported so consumers can render the\n * methodology; override semantics may come in a future minor release.\n */\nexport const ASSESSMENT_LEVEL_WEIGHTS: Record<AssessmentLevel, number> = {\n not_indicated: 0,\n reported: 0.25,\n documented: 0.5,\n measured: 0.75,\n confirmed: 1,\n};\n\n/**\n * OCS id prefixes treated as prosaic (well-explained). A confident *prosaic*\n * classification means the case is explained, so it contributes no anomaly\n * signal. Overridable per {@link score} call. Default is the OCS \"Prosaic\"\n * branch (`1.1.1`).\n */\nexport const DEFAULT_PROSAIC_OCS_PREFIXES: readonly string[] = ['1.1.1'];\n\nexport const CompellingnessWeightsSchema = z\n .object({\n technology: z.number().min(0).describe('Weight of the technology-anomaly signal.'),\n biologics: z.number().min(0).describe('Weight of the biologics-anomaly signal.'),\n origin: z.number().min(0).describe('Weight of the (non-prosaic) origin-confidence signal.'),\n })\n .meta({ id: 'CompellingnessWeights' })\n .describe('Relative weights for the compellingness components. Need not sum to 1 — normalized internally.');\n\nexport type CompellingnessWeights = z.infer<typeof CompellingnessWeightsSchema>;\n\n/**\n * Fixed default weights, exposed as a transparent, overridable data object (not a\n * config system). Partners can propose alternate weight sets without an API change.\n *\n * @experimental The *shape* (`CompellingnessWeights`) is stable, but these specific\n * default values are a reference methodology that may be re-tuned in a minor release.\n * Pin your own `weights` via {@link ScoreOptions} if you need stable numbers.\n */\nexport const DEFAULT_WEIGHTS: CompellingnessWeights = {\n technology: 0.45,\n biologics: 0.35,\n origin: 0.2,\n};\n\nexport const ScoreResultSchema = z\n .object({\n score: z.number().min(0).max(1).describe('Overall compellingness (consensus point estimate), 0–1.'),\n range: z\n .object({\n low: z.number().min(0).max(1).describe('Most-skeptical reading of the strongest-anomaly signal across competing claims.'),\n high: z.number().min(0).max(1).describe('Most-anomalous reading of the strongest-anomaly signal across competing claims.'),\n })\n .describe(\n 'Spread of the score across competing claims — wide range signals disagreement. Bounds are '\n + 'computed per-signal under the same strongest-anomaly (max) reduction as the point estimate, '\n + 'not a global min/max over every claim.',\n ),\n contested: z\n .boolean()\n .describe('True when claims disagree on *direction* (some assert anomaly, some assert none).'),\n components: z\n .object({\n technology: z.number().min(0).max(1),\n biologics: z.number().min(0).max(1),\n origin: z.number().min(0).max(1),\n })\n .describe('Per-signal consensus contributions before weighting.'),\n weights: CompellingnessWeightsSchema,\n scoringVersion: z.string().describe('Methodology version that produced this score.'),\n })\n .meta({ id: 'ScoreResult' })\n .describe('A reference compellingness score for an observation, with a contestedness range.');\n\nexport type ScoreResult = z.infer<typeof ScoreResultSchema>;\n\n/** The minimal claim shape the trust hook sees (it never needs the full body). */\nexport type EvaluatorWeightInput = {\n evaluatedBy?: string | undefined;\n evidenceRefs?: string[] | undefined;\n};\n\nexport interface ScoreOptions {\n weights?: CompellingnessWeights;\n /** Override the prosaic OCS prefixes (defaults to {@link DEFAULT_PROSAIC_OCS_PREFIXES}). */\n prosaicPrefixes?: readonly string[];\n /**\n * Trust applied to each claim's vote in the consensus point estimate (default\n * 1.0 — uniform). This is where a future trust policy (e.g.\n * weighting verified/official evaluators higher) plugs in here without changing\n * the formula. Range/contestedness are NOT affected — they report the honest\n * spread of what evaluators actually asserted.\n *\n * @experimental The hook signature is stable, but the default trust *policy*\n * (uniform 1.0) is deliberately unopinionated and is a separate, ongoing design\n * task; a future built-in policy may change the default.\n */\n evaluatorWeight?: (claim: EvaluatorWeightInput) => number;\n}\n\nconst UNIFORM_TRUST = (): number => 1;\n\nfunction isProsaic(id: string, prefixes: readonly string[]): boolean {\n return prefixes.some((p) => id === p || id.startsWith(`${p}.`));\n}\n\nfunction round(n: number): number {\n return Math.round(n * 1e4) / 1e4;\n}\n\ntype Weighted = { magnitude: number; weight: number };\n\ninterface ClaimStats {\n /** Trust-weighted mean magnitude (the consensus point). */\n point: number;\n /** Min asserted magnitude (most skeptical). */\n low: number;\n /** Max asserted magnitude (most anomalous). */\n high: number;\n /** Claims disagree on direction (some > 0, some === 0). */\n contested: boolean;\n}\n\nconst EMPTY_STATS: ClaimStats = { point: 0, low: 0, high: 0, contested: false };\n\nfunction reduceClaims(weighted: Weighted[]): ClaimStats {\n if (weighted.length === 0) return EMPTY_STATS;\n let wsum = 0;\n let msum = 0;\n let low = Infinity;\n let high = -Infinity;\n let anyAnomaly = false;\n let anyNone = false;\n for (const { magnitude, weight } of weighted) {\n wsum += weight;\n msum += magnitude * weight;\n if (magnitude < low) low = magnitude;\n if (magnitude > high) high = magnitude;\n if (magnitude > 0) anyAnomaly = true;\n else anyNone = true;\n }\n return {\n point: wsum > 0 ? msum / wsum : 0,\n low,\n high,\n contested: anyAnomaly && anyNone,\n };\n}\n\n/** Asserted anomaly magnitude of one observable claim, in [0,1]. */\nfunction observableMagnitude(claim: ObservableClaim): number {\n return ASSESSMENT_LEVEL_WEIGHTS[claim.level] * (claim.confidence ?? 1);\n}\n\n/**\n * Strongest established anomaly within a domain — the \"is there *any* anomaly?\" reduction.\n * `point`/`low`/`high` are each a **max across observables** of that observable's claim-list\n * stat. So `low` is \"the most-skeptical reading of the strongest-anomaly observable,\" not a\n * global floor across all observables — consistent with the point estimate's max reduction.\n */\nfunction domainStats(\n group: Partial<Record<string, ObservableClaim[]>> | undefined,\n evaluatorWeight: (claim: EvaluatorWeightInput) => number,\n): ClaimStats {\n if (!group) return EMPTY_STATS;\n let point = 0;\n let low = 0;\n let high = 0;\n let contested = false;\n for (const claims of Object.values(group)) {\n if (!claims || claims.length === 0) continue;\n const weighted = claims.map<Weighted>((c) => ({\n magnitude: observableMagnitude(c),\n weight: (c.confidence ?? 1) * evaluatorWeight(c),\n }));\n const stats = reduceClaims(weighted);\n if (stats.point > point) point = stats.point;\n if (stats.low > low) low = stats.low;\n if (stats.high > high) high = stats.high;\n if (stats.contested) contested = true;\n }\n return { point, low, high, contested };\n}\n\n/** Confidence in a *non-prosaic* origin, reduced across competing classifications. */\nfunction originStats(\n origin: OriginClaim[] | undefined,\n prefixes: readonly string[],\n evaluatorWeight: (claim: EvaluatorWeightInput) => number,\n): ClaimStats {\n if (!origin || origin.length === 0) return EMPTY_STATS;\n const weighted = origin.map<Weighted>((c) => ({\n magnitude: isProsaic(c.primaryHypothesis, prefixes) ? 0 : c.confidence,\n weight: c.confidence * evaluatorWeight(c),\n }));\n return reduceClaims(weighted);\n}\n\n/**\n * Reference compellingness scorer. Combines the strongest technology- and\n * biologics-anomaly signals (from `observableAssessments`) with the confidence in\n * a *non-prosaic* origin classification (from `origin`), under transparent,\n * overridable weights. Each slot is a *list* of competing claims, so the scorer\n * reports a consensus `score`, the `range` of readings across claims, and whether\n * the case is `contested` (evaluators disagree on direction). This answers \"how\n * strongly does this case bear on the two public questions — anomalous\n * tech/biologics, and a non-mundane origin?\" — distinct from completeness.\n */\nexport function score(observation: Observation, options?: ScoreOptions): ScoreResult {\n const weights = options?.weights ?? DEFAULT_WEIGHTS;\n const prosaicPrefixes = options?.prosaicPrefixes ?? DEFAULT_PROSAIC_OCS_PREFIXES;\n const evaluatorWeight = options?.evaluatorWeight ?? UNIFORM_TRUST;\n\n const assessments = observation.observableAssessments;\n const technology = domainStats(assessments?.technology, evaluatorWeight);\n const biologics = domainStats(assessments?.biologics, evaluatorWeight);\n const origin = originStats(observation.origin, prosaicPrefixes, evaluatorWeight);\n\n const totalWeight = weights.technology + weights.biologics + weights.origin;\n const combine = (t: number, b: number, o: number): number =>\n totalWeight > 0\n ? (t * weights.technology + b * weights.biologics + o * weights.origin) / totalWeight\n : 0;\n\n return {\n score: round(combine(technology.point, biologics.point, origin.point)),\n range: {\n low: round(combine(technology.low, biologics.low, origin.low)),\n high: round(combine(technology.high, biologics.high, origin.high)),\n },\n contested: technology.contested || biologics.contested || origin.contested,\n components: {\n technology: round(technology.point),\n biologics: round(biologics.point),\n origin: round(origin.point),\n },\n weights,\n scoringVersion: SCORING_VERSION,\n };\n}\n\nexport interface RankedObservation<T extends Observation = Observation> {\n observation: T;\n result: ScoreResult;\n}\n\n/**\n * Score a set of observations and return them sorted most-compelling first — the\n * \"float the strongest cases to the top\" use case.\n */\nexport function rankByCompellingness<T extends Observation>(\n observations: readonly T[],\n options?: ScoreOptions,\n): RankedObservation<T>[] {\n return observations\n .map((observation) => ({ observation, result: score(observation, options) }))\n .sort((a, b) => b.result.score - a.result.score);\n}\n","import { z } from 'zod';\nimport { jsonSchemaEnvelope, schemaId } from '@disclosureos/records/shared';\nimport { CompletenessResultSchema } from './completeness';\nimport { ScoreResultSchema, CompellingnessWeightsSchema } from './compellingness';\n\nexport const SCORING_SCHEMA_VERSION = '2.0.0';\n\nexport const SCORING_SCHEMA_ID = schemaId('scoring', 'scoring', SCORING_SCHEMA_VERSION);\n\n/**\n * Emit the JSON Schema (draft 2020-12) for the scoring output shapes\n * ({@link CompletenessResultSchema}, {@link ScoreResultSchema}) so downstream\n * consumers (and other languages) can validate stored scores.\n */\nexport function scoringJsonSchema(): Record<string, unknown> {\n const bundle = z.toJSONSchema(\n z.object({\n completeness: CompletenessResultSchema,\n compellingness: ScoreResultSchema,\n weights: CompellingnessWeightsSchema,\n }),\n { target: 'draft-2020-12' },\n ) as Record<string, unknown>;\n return jsonSchemaEnvelope(bundle, { id: SCORING_SCHEMA_ID, version: SCORING_SCHEMA_VERSION });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/completeness/field-paths.ts","../src/completeness/index.ts","../src/compellingness/calibration.ts","../src/compellingness/index.ts","../src/schema.ts"],"names":["z"],"mappings":"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","file":"index.js","sourcesContent":["import { recordsJsonSchema } from '@disclosureos/records';\n\n/** A leaf field path derived from the records JSON Schema. */\nexport interface FieldPath {\n /** Dot-path into an `Observation` (e.g. `location.latitude`). */\n path: string;\n /** True only when every segment along the path is schema-required. */\n required: boolean;\n}\n\ninterface SchemaNode {\n $ref?: string;\n properties?: Record<string, SchemaNode>;\n required?: string[];\n type?: string | string[];\n}\n\nfunction refName(ref: string): string {\n return ref.replace('#/$defs/', '');\n}\n\n/**\n * Walk the committed records JSON Schema and enumerate every leaf field path on\n * `Observation`. Objects are recursed (resolving `$ref` into `$defs`); arrays,\n * enums, scalars, and `record`/`anyOf` nodes are treated as leaves. This is the\n * single source of completeness coverage — there is no hand-maintained path list\n * (so adding a field to the schema automatically extends completeness).\n */\nexport function deriveFieldPaths(schema?: Record<string, unknown>): FieldPath[] {\n const root = (schema ?? recordsJsonSchema()) as Record<string, unknown> & {\n $ref?: string;\n $defs?: Record<string, SchemaNode>;\n };\n const defs = root.$defs ?? {};\n const out: FieldPath[] = [];\n\n function resolve(node: SchemaNode): SchemaNode {\n let cur: SchemaNode = node;\n const seen = new Set<string>();\n while (cur.$ref) {\n const name = refName(cur.$ref);\n if (seen.has(name)) break;\n seen.add(name);\n cur = defs[name] ?? {};\n }\n return cur;\n }\n\n function walk(node: SchemaNode, prefix: string, required: boolean, visited: Set<string>): void {\n const refn = node.$ref ? refName(node.$ref) : undefined;\n // Cycle guard: a recursive $ref re-entering an in-progress def becomes a leaf.\n if (refn && visited.has(refn)) {\n if (prefix) out.push({ path: prefix, required });\n return;\n }\n const resolved = resolve(node);\n const props = resolved.properties;\n if (props && Object.keys(props).length > 0) {\n const nextVisited = refn ? new Set([...visited, refn]) : visited;\n const req = new Set(resolved.required ?? []);\n for (const [key, child] of Object.entries(props)) {\n const childPath = prefix ? `${prefix}.${key}` : key;\n walk(child, childPath, required && req.has(key), nextVisited);\n }\n return;\n }\n if (prefix) out.push({ path: prefix, required });\n }\n\n if (root.$ref) {\n walk({ $ref: root.$ref }, '', true, new Set());\n }\n return out;\n}\n","import { z } from 'zod';\nimport type { Observation } from '@disclosureos/records';\nimport { deriveFieldPaths } from './field-paths';\nimport type { FieldPath } from './field-paths';\n\nexport type { FieldPath } from './field-paths';\nexport { deriveFieldPaths } from './field-paths';\n\nexport const CompletenessResultSchema = z\n .object({\n total: z.number().int().describe('Total leaf field paths defined by the records schema.'),\n present: z.number().int().describe('How many of those paths carry a value.'),\n percentage: z.number().int().describe('present / total, as a 0–100 integer.'),\n missing: z.array(z.string()).describe('Paths that are absent or empty.'),\n requiredTotal: z.number().int().describe('Count of schema-required leaf paths.'),\n requiredPresent: z.number().int().describe('How many required paths carry a value.'),\n requiredPercentage: z\n .number()\n .int()\n .describe('requiredPresent / requiredTotal, as a 0–100 integer.'),\n })\n .meta({ id: 'CompletenessResult' })\n .describe('How fully an observation populates the records schema.');\n\nexport type CompletenessResult = z.infer<typeof CompletenessResultSchema>;\n\nexport interface CompletenessOptions {\n /** Override the derived field-path set (defaults to the records schema). */\n paths?: FieldPath[];\n}\n\nfunction getNested(obj: Record<string, unknown>, path: string): unknown {\n let current: unknown = obj;\n for (const part of path.split('.')) {\n if (current == null || typeof current !== 'object') return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\nfunction isPresent(value: unknown): boolean {\n if (value == null) return false;\n if (typeof value === 'string') return value.length > 0;\n if (Array.isArray(value)) return value.length > 0;\n if (typeof value === 'object') return Object.keys(value).length > 0;\n return true;\n}\n\n// Field paths are stable for a given schema version, so derive once and reuse.\nlet cachedPaths: FieldPath[] | null = null;\nfunction defaultPaths(): FieldPath[] {\n return (cachedPaths ??= deriveFieldPaths());\n}\n\nfunction pct(present: number, total: number): number {\n return total > 0 ? Math.round((present / total) * 100) : 0;\n}\n\n/**\n * Measure how fully an observation populates the records schema. This is the\n * \"is it well-documented?\" axis — deliberately kept separate from compellingness\n * (\"is it anomalous?\"), so a thin-but-anomalous case never masquerades as complete.\n */\nexport function getCompleteness(\n observation: Observation | Record<string, unknown>,\n options?: CompletenessOptions,\n): CompletenessResult {\n const record = observation as Record<string, unknown>;\n const paths = options?.paths ?? defaultPaths();\n const missing: string[] = [];\n let present = 0;\n let requiredTotal = 0;\n let requiredPresent = 0;\n\n for (const field of paths) {\n const ok = isPresent(getNested(record, field.path));\n if (ok) present++;\n else missing.push(field.path);\n if (field.required) {\n requiredTotal++;\n if (ok) requiredPresent++;\n }\n }\n\n return {\n total: paths.length,\n present,\n percentage: pct(present, paths.length),\n missing,\n requiredTotal,\n requiredPresent,\n requiredPercentage: pct(requiredPresent, requiredTotal),\n };\n}\n","import type { Observation } from '@disclosureos/records';\nimport { parseEvidenceRef, parseSensorRef } from '@disclosureos/records/shared';\nimport type { CalibrationStatus, SensorManifest } from '@disclosureos/instruments';\nimport type { EvaluatorWeightInput } from './index';\n\n/**\n * Trust multiplier per calibration maturity level, plus the `unresolved`\n * baseline for claims whose sensor evidence does not resolve to a manifest\n * entry (no `sensorRef`, unknown org, unknown sensor, or no calibration block).\n */\nexport type CalibrationTrustWeights = Record<CalibrationStatus | 'unresolved', number>;\n\n/**\n * Conservative default gradient: instruments with real calibration provenance\n * earn *credit* over unresolved sensors — nothing is ever penalized below the\n * baseline, so adopting the hook can only reward organizations that publish\n * manifests, never punish records that predate them.\n *\n * @experimental The shape is stable; these specific values are a reference\n * policy that may be re-tuned in a minor release. Pin your own `weights` via\n * {@link CalibrationTrustOptions} if you need stable numbers.\n */\nexport const CALIBRATION_TRUST_WEIGHTS: CalibrationTrustWeights = {\n unresolved: 1,\n none: 1,\n candidate_identified: 1.05,\n in_practice: 1.15,\n documented: 1.25,\n};\n\nexport interface CalibrationTrustOptions {\n /** Override the trust gradient (defaults to {@link CALIBRATION_TRUST_WEIGHTS}). */\n weights?: CalibrationTrustWeights;\n /**\n * A base trust policy composed (multiplied) with the calibration credit —\n * e.g. an evaluator-identity policy. Defaults to uniform 1.0.\n */\n evaluatorWeight?: (claim: EvaluatorWeightInput) => number;\n}\n\n/**\n * Build an `evaluatorWeight` hook (see `ScoreOptions.evaluatorWeight`) that\n * credits claims backed by instruments with published calibration provenance.\n *\n * Resolution chain, per claim: `evidenceRefs` (`\"sensor:<id>\"`) → the\n * observation's `sensorEvidence.sensors[]` reading → its `sensorRef`\n * (`\"<org-slug>:<sensor-id>\"`) → the matching manifest entry's\n * `calibration.status`. When a claim cites several sensor readings, the\n * strongest instrument backing wins. Claims with no resolvable sensor\n * evidence get the `unresolved` baseline.\n *\n * Like every `evaluatorWeight` policy, this shifts only the consensus point\n * estimate — `range` and `contested` still report the honest spread of what\n * evaluators actually asserted.\n *\n * ```ts\n * const result = score(observation, {\n * evaluatorWeight: calibrationTrust(observation, [navyManifest]),\n * });\n * ```\n */\nexport function calibrationTrust(\n observation: Observation,\n manifests: readonly SensorManifest[],\n options?: CalibrationTrustOptions,\n): (claim: EvaluatorWeightInput) => number {\n const weights = options?.weights ?? CALIBRATION_TRUST_WEIGHTS;\n const base = options?.evaluatorWeight ?? ((): number => 1);\n\n // Pre-resolve each in-record sensor reading to its manifest calibration status.\n const statusBySensorId = new Map<string, CalibrationStatus>();\n for (const reading of observation.sensorEvidence?.sensors ?? []) {\n if (!reading.sensorRef) continue;\n const ref = parseSensorRef(reading.sensorRef);\n if (!ref) continue;\n const entry = manifests\n .find((manifest) => manifest.orgSlug === ref.orgSlug)\n ?.sensors.find((sensor) => sensor.id === ref.sensorId);\n const status = entry?.calibration?.status;\n if (status) statusBySensorId.set(reading.id, status);\n }\n\n return (claim) => {\n let credit = weights.unresolved;\n for (const evidence of claim.evidenceRefs ?? []) {\n const parsed = parseEvidenceRef(evidence);\n if (!parsed || parsed.kind !== 'sensor') continue;\n const status = statusBySensorId.get(parsed.id);\n const weight = status === undefined ? weights.unresolved : weights[status];\n if (weight > credit) credit = weight;\n }\n return base(claim) * credit;\n };\n}\n","import { z } from 'zod';\nimport type { Observation } from '@disclosureos/records';\nimport type { AssessmentLevel, ObservableClaim } from '@disclosureos/observables';\nimport type { OriginClaim } from '@disclosureos/origins';\n// Side-effect imports register the `observableAssessments` and `origin` slots on\n// `Observation` via module augmentation, so they are typed below.\nimport '@disclosureos/observables';\nimport '@disclosureos/origins';\n\nexport { calibrationTrust, CALIBRATION_TRUST_WEIGHTS } from './calibration';\nexport type { CalibrationTrustWeights, CalibrationTrustOptions } from './calibration';\n\n/** Methodology version stamped on every {@link ScoreResult}. Bump on weight/formula changes. */\nexport const SCORING_VERSION = '2.0.0' as const;\n\n/**\n * Ordinal anomaly strength per evidentiary tier. A `confirmed` signal counts\n * fully; `not_indicated` contributes nothing. Multiplied by the assessment's\n * confidence (default 1 when unstated). Exported so consumers can render the\n * methodology; override semantics may come in a future minor release.\n */\nexport const ASSESSMENT_LEVEL_WEIGHTS: Record<AssessmentLevel, number> = {\n not_indicated: 0,\n reported: 0.25,\n documented: 0.5,\n measured: 0.75,\n confirmed: 1,\n};\n\n/**\n * OCS id prefixes treated as prosaic (well-explained). A confident *prosaic*\n * classification means the case is explained, so it contributes no anomaly\n * signal. Overridable per {@link score} call. Default is the OCS \"Prosaic\"\n * branch (`1.1.1`).\n */\nexport const DEFAULT_PROSAIC_OCS_PREFIXES: readonly string[] = ['1.1.1'];\n\nexport const CompellingnessWeightsSchema = z\n .object({\n technology: z.number().min(0).describe('Weight of the technology-anomaly signal.'),\n biologics: z.number().min(0).describe('Weight of the biologics-anomaly signal.'),\n origin: z.number().min(0).describe('Weight of the (non-prosaic) origin-confidence signal.'),\n })\n .meta({ id: 'CompellingnessWeights' })\n .describe('Relative weights for the compellingness components. Need not sum to 1 — normalized internally.');\n\nexport type CompellingnessWeights = z.infer<typeof CompellingnessWeightsSchema>;\n\n/**\n * Fixed default weights, exposed as a transparent, overridable data object (not a\n * config system). Partners can propose alternate weight sets without an API change.\n *\n * @experimental The *shape* (`CompellingnessWeights`) is stable, but these specific\n * default values are a reference methodology that may be re-tuned in a minor release.\n * Pin your own `weights` via {@link ScoreOptions} if you need stable numbers.\n */\nexport const DEFAULT_WEIGHTS: CompellingnessWeights = {\n technology: 0.45,\n biologics: 0.35,\n origin: 0.2,\n};\n\nexport const ScoreResultSchema = z\n .object({\n score: z.number().min(0).max(1).describe('Overall compellingness (consensus point estimate), 0–1.'),\n range: z\n .object({\n low: z.number().min(0).max(1).describe('Most-skeptical reading of the strongest-anomaly signal across competing claims.'),\n high: z.number().min(0).max(1).describe('Most-anomalous reading of the strongest-anomaly signal across competing claims.'),\n })\n .describe(\n 'Spread of the score across competing claims — wide range signals disagreement. Bounds are '\n + 'computed per-signal under the same strongest-anomaly (max) reduction as the point estimate, '\n + 'not a global min/max over every claim.',\n ),\n contested: z\n .boolean()\n .describe('True when claims disagree on *direction* (some assert anomaly, some assert none).'),\n components: z\n .object({\n technology: z.number().min(0).max(1),\n biologics: z.number().min(0).max(1),\n origin: z.number().min(0).max(1),\n })\n .describe('Per-signal consensus contributions before weighting.'),\n weights: CompellingnessWeightsSchema,\n scoringVersion: z.string().describe('Methodology version that produced this score.'),\n })\n .meta({ id: 'ScoreResult' })\n .describe('A reference compellingness score for an observation, with a contestedness range.');\n\nexport type ScoreResult = z.infer<typeof ScoreResultSchema>;\n\n/** The minimal claim shape the trust hook sees (it never needs the full body). */\nexport type EvaluatorWeightInput = {\n evaluatedBy?: string | undefined;\n evidenceRefs?: string[] | undefined;\n};\n\nexport interface ScoreOptions {\n weights?: CompellingnessWeights;\n /** Override the prosaic OCS prefixes (defaults to {@link DEFAULT_PROSAIC_OCS_PREFIXES}). */\n prosaicPrefixes?: readonly string[];\n /**\n * Trust applied to each claim's vote in the consensus point estimate (default\n * 1.0 — uniform). This is where a future trust policy (e.g.\n * weighting verified/official evaluators higher) plugs in here without changing\n * the formula. Range/contestedness are NOT affected — they report the honest\n * spread of what evaluators actually asserted.\n *\n * @experimental The hook signature is stable, but the default trust *policy*\n * (uniform 1.0) is deliberately unopinionated and is a separate, ongoing design\n * task; a future built-in policy may change the default.\n */\n evaluatorWeight?: (claim: EvaluatorWeightInput) => number;\n}\n\nconst UNIFORM_TRUST = (): number => 1;\n\nfunction isProsaic(id: string, prefixes: readonly string[]): boolean {\n return prefixes.some((p) => id === p || id.startsWith(`${p}.`));\n}\n\nfunction round(n: number): number {\n return Math.round(n * 1e4) / 1e4;\n}\n\ntype Weighted = { magnitude: number; weight: number };\n\ninterface ClaimStats {\n /** Trust-weighted mean magnitude (the consensus point). */\n point: number;\n /** Min asserted magnitude (most skeptical). */\n low: number;\n /** Max asserted magnitude (most anomalous). */\n high: number;\n /** Claims disagree on direction (some > 0, some === 0). */\n contested: boolean;\n}\n\nconst EMPTY_STATS: ClaimStats = { point: 0, low: 0, high: 0, contested: false };\n\nfunction reduceClaims(weighted: Weighted[]): ClaimStats {\n if (weighted.length === 0) return EMPTY_STATS;\n let wsum = 0;\n let msum = 0;\n let low = Infinity;\n let high = -Infinity;\n let anyAnomaly = false;\n let anyNone = false;\n for (const { magnitude, weight } of weighted) {\n wsum += weight;\n msum += magnitude * weight;\n if (magnitude < low) low = magnitude;\n if (magnitude > high) high = magnitude;\n if (magnitude > 0) anyAnomaly = true;\n else anyNone = true;\n }\n return {\n point: wsum > 0 ? msum / wsum : 0,\n low,\n high,\n contested: anyAnomaly && anyNone,\n };\n}\n\n/** Asserted anomaly magnitude of one observable claim, in [0,1]. */\nfunction observableMagnitude(claim: ObservableClaim): number {\n return ASSESSMENT_LEVEL_WEIGHTS[claim.level] * (claim.confidence ?? 1);\n}\n\n/**\n * Strongest established anomaly within a domain — the \"is there *any* anomaly?\" reduction.\n * `point`/`low`/`high` are each a **max across observables** of that observable's claim-list\n * stat. So `low` is \"the most-skeptical reading of the strongest-anomaly observable,\" not a\n * global floor across all observables — consistent with the point estimate's max reduction.\n */\nfunction domainStats(\n group: Partial<Record<string, ObservableClaim[]>> | undefined,\n evaluatorWeight: (claim: EvaluatorWeightInput) => number,\n): ClaimStats {\n if (!group) return EMPTY_STATS;\n let point = 0;\n let low = 0;\n let high = 0;\n let contested = false;\n for (const claims of Object.values(group)) {\n if (!claims || claims.length === 0) continue;\n const weighted = claims.map<Weighted>((c) => ({\n magnitude: observableMagnitude(c),\n weight: (c.confidence ?? 1) * evaluatorWeight(c),\n }));\n const stats = reduceClaims(weighted);\n if (stats.point > point) point = stats.point;\n if (stats.low > low) low = stats.low;\n if (stats.high > high) high = stats.high;\n if (stats.contested) contested = true;\n }\n return { point, low, high, contested };\n}\n\n/** Confidence in a *non-prosaic* origin, reduced across competing classifications. */\nfunction originStats(\n origin: OriginClaim[] | undefined,\n prefixes: readonly string[],\n evaluatorWeight: (claim: EvaluatorWeightInput) => number,\n): ClaimStats {\n if (!origin || origin.length === 0) return EMPTY_STATS;\n const weighted = origin.map<Weighted>((c) => ({\n magnitude: isProsaic(c.primaryHypothesis, prefixes) ? 0 : c.confidence,\n weight: c.confidence * evaluatorWeight(c),\n }));\n return reduceClaims(weighted);\n}\n\n/**\n * Reference compellingness scorer. Combines the strongest technology- and\n * biologics-anomaly signals (from `observableAssessments`) with the confidence in\n * a *non-prosaic* origin classification (from `origin`), under transparent,\n * overridable weights. Each slot is a *list* of competing claims, so the scorer\n * reports a consensus `score`, the `range` of readings across claims, and whether\n * the case is `contested` (evaluators disagree on direction). This answers \"how\n * strongly does this case bear on the two public questions — anomalous\n * tech/biologics, and a non-mundane origin?\" — distinct from completeness.\n */\nexport function score(observation: Observation, options?: ScoreOptions): ScoreResult {\n const weights = options?.weights ?? DEFAULT_WEIGHTS;\n const prosaicPrefixes = options?.prosaicPrefixes ?? DEFAULT_PROSAIC_OCS_PREFIXES;\n const evaluatorWeight = options?.evaluatorWeight ?? UNIFORM_TRUST;\n\n const assessments = observation.observableAssessments;\n const technology = domainStats(assessments?.technology, evaluatorWeight);\n const biologics = domainStats(assessments?.biologics, evaluatorWeight);\n const origin = originStats(observation.origin, prosaicPrefixes, evaluatorWeight);\n\n const totalWeight = weights.technology + weights.biologics + weights.origin;\n const combine = (t: number, b: number, o: number): number =>\n totalWeight > 0\n ? (t * weights.technology + b * weights.biologics + o * weights.origin) / totalWeight\n : 0;\n\n return {\n score: round(combine(technology.point, biologics.point, origin.point)),\n range: {\n low: round(combine(technology.low, biologics.low, origin.low)),\n high: round(combine(technology.high, biologics.high, origin.high)),\n },\n contested: technology.contested || biologics.contested || origin.contested,\n components: {\n technology: round(technology.point),\n biologics: round(biologics.point),\n origin: round(origin.point),\n },\n weights,\n scoringVersion: SCORING_VERSION,\n };\n}\n\nexport interface RankedObservation<T extends Observation = Observation> {\n observation: T;\n result: ScoreResult;\n}\n\n/**\n * Score a set of observations and return them sorted most-compelling first — the\n * \"float the strongest cases to the top\" use case.\n */\nexport function rankByCompellingness<T extends Observation>(\n observations: readonly T[],\n options?: ScoreOptions,\n): RankedObservation<T>[] {\n return observations\n .map((observation) => ({ observation, result: score(observation, options) }))\n .sort((a, b) => b.result.score - a.result.score);\n}\n","import { z } from 'zod';\nimport { jsonSchemaEnvelope, schemaId } from '@disclosureos/records/shared';\nimport { CompletenessResultSchema } from './completeness';\nimport { ScoreResultSchema, CompellingnessWeightsSchema } from './compellingness';\n\nexport const SCORING_SCHEMA_VERSION = '2.0.0';\n\nexport const SCORING_SCHEMA_ID = schemaId('scoring', 'scoring', SCORING_SCHEMA_VERSION);\n\n/**\n * Emit the JSON Schema (draft 2020-12) for the scoring output shapes\n * ({@link CompletenessResultSchema}, {@link ScoreResultSchema}) so downstream\n * consumers (and other languages) can validate stored scores.\n */\nexport function scoringJsonSchema(): Record<string, unknown> {\n const bundle = z.toJSONSchema(\n z.object({\n completeness: CompletenessResultSchema,\n compellingness: ScoreResultSchema,\n weights: CompellingnessWeightsSchema,\n }),\n { target: 'draft-2020-12' },\n ) as Record<string, unknown>;\n return jsonSchemaEnvelope(bundle, { id: SCORING_SCHEMA_ID, version: SCORING_SCHEMA_VERSION });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@disclosureos/scoring",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Reference scoring for the DisclosureOS ecosystem — record completeness and case compellingness across the Observables and Origin layers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -51,9 +51,10 @@
|
|
|
51
51
|
],
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"zod": "^4.4.3",
|
|
54
|
-
"@disclosureos/
|
|
55
|
-
"@disclosureos/
|
|
56
|
-
"@disclosureos/
|
|
54
|
+
"@disclosureos/instruments": "1.0.0",
|
|
55
|
+
"@disclosureos/observables": "1.0.1",
|
|
56
|
+
"@disclosureos/origins": "1.0.1",
|
|
57
|
+
"@disclosureos/records": "1.1.0"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
59
60
|
"@types/node": "^20",
|