@disclosureos/scoring 1.0.0 → 2.0.0-beta.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 +54 -0
- package/README.md +50 -11
- 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/completeness/index.d.ts +3 -3
- package/dist/completeness/index.js.map +1 -1
- package/dist/experimental/v2/index.d.ts +152 -0
- package/dist/experimental/v2/index.js +433 -0
- package/dist/experimental/v2/index.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +35 -2
- package/dist/index.js.map +1 -1
- package/dist/population-coverage/index.d.ts +3 -0
- package/dist/population-coverage/index.js +109 -0
- package/dist/population-coverage/index.js.map +1 -0
- package/package.json +19 -7
- package/schema/experimental/assessment-summary-evaluation-0.1.0.schema.json +174 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,14 +1,68 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.0-beta.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- Prepare the coordinated v2 beta package set. This prerelease packages the merged
|
|
8
|
+
experimental observation and claim contracts, acquisition and dataset exchange,
|
|
9
|
+
versioned evaluation, and bounded migration tooling for external consumer testing.
|
|
10
|
+
Existing root APIs and schema identities remain available; the new contracts keep
|
|
11
|
+
explicit `/experimental/v2` imports. Observables and origins retain their existing
|
|
12
|
+
catalogs within the pinned beta dependency graph. This is not a stable v2 release,
|
|
13
|
+
scientific certification, or publication of partner data. See
|
|
14
|
+
`docs/releases/2.0.0-beta.0.md` for package-specific scope and validation boundaries.
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated dependencies
|
|
19
|
+
- @disclosureos/records@2.0.0-beta.0
|
|
20
|
+
- @disclosureos/observables@2.0.0-beta.0
|
|
21
|
+
- @disclosureos/origins@2.0.0-beta.0
|
|
22
|
+
- @disclosureos/instruments@2.0.0-beta.0
|
|
23
|
+
|
|
3
24
|
All notable changes to `@disclosureos/scoring` are documented here. The format is
|
|
4
25
|
based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project
|
|
5
26
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
27
|
|
|
28
|
+
## Unreleased compatibility additions
|
|
29
|
+
|
|
30
|
+
- Add descriptive population-coverage naming while preserving legacy completeness results and schema identities.
|
|
31
|
+
|
|
32
|
+
## Unreleased experimental work
|
|
33
|
+
|
|
34
|
+
- Add exact-byte assessment-summary provenance, versioned input schema, and unsigned computed-result receipts; existing declaration summary behavior and source-verification limits are preserved.
|
|
35
|
+
|
|
36
|
+
- Add opt-in assessment summaries with current revision filtering, exact declaration deduplication, explicit shared lineage, and distinct outcome labels. Missing confidence stays omitted; existing scoring APIs and package versions are unchanged.
|
|
37
|
+
|
|
38
|
+
## 1.1.0
|
|
39
|
+
|
|
40
|
+
### Minor Changes
|
|
41
|
+
|
|
42
|
+
- 19bad3d: Add `calibrationTrust` — calibration provenance from published sensor manifests
|
|
43
|
+
(`@disclosureos/instruments`) as a scoring input. Builds an `evaluatorWeight`
|
|
44
|
+
policy that credits claims whose sensor evidence resolves (via
|
|
45
|
+
`SensorReading.sensorRef`) to a manifest entry with real calibration
|
|
46
|
+
provenance, under an overridable `CALIBRATION_TRUST_WEIGHTS` gradient
|
|
47
|
+
(`unresolved`/`none` 1.0 baseline → `documented` 1.25). Conservative by
|
|
48
|
+
default: unresolved sensors are never penalized, and only the consensus point
|
|
49
|
+
shifts — `range` and `contested` still report the honest spread across claims.
|
|
50
|
+
|
|
51
|
+
### Patch Changes
|
|
52
|
+
|
|
53
|
+
- Updated dependencies [b373962]
|
|
54
|
+
- Updated dependencies [e6ff99c]
|
|
55
|
+
- @disclosureos/instruments@1.0.0
|
|
56
|
+
- @disclosureos/records@1.1.0
|
|
57
|
+
- @disclosureos/observables@1.0.1
|
|
58
|
+
- @disclosureos/origins@1.0.1
|
|
59
|
+
|
|
7
60
|
## 1.0.0
|
|
8
61
|
|
|
9
62
|
Initial public release — the Scoring layer.
|
|
10
63
|
|
|
11
64
|
### Added
|
|
65
|
+
|
|
12
66
|
- `getCompleteness` (how well-documented a record is) and `score` /
|
|
13
67
|
`rankByCompellingness` (how anomalous a case is).
|
|
14
68
|
- Scoring over arrays of competing claims: results include a `range` (spread across
|
package/README.md
CHANGED
|
@@ -3,17 +3,16 @@
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
4
|
|
|
5
5
|
Reference scoring for the [DisclosureOS](https://github.com/disclosurefoundation)
|
|
6
|
-
ecosystem — the **Scoring** layer.
|
|
7
|
-
orthogonal measures:
|
|
6
|
+
ecosystem — the **Scoring** layer. Its legacy `Observation` APIs provide:
|
|
8
7
|
|
|
9
|
-
- **
|
|
10
|
-
|
|
8
|
+
- **Population coverage** — the fraction of legacy schema field paths carrying a value.
|
|
9
|
+
Presence does not establish documentation quality or analytical readiness.
|
|
11
10
|
- **Compellingness** — *is it anomalous / non-mundane?* Derived from the observable
|
|
12
11
|
assessments and origin classification.
|
|
13
12
|
|
|
14
13
|
## The DisclosureOS model
|
|
15
14
|
|
|
16
|
-
DisclosureOS is a **
|
|
15
|
+
DisclosureOS is a **six-part standard**. Each part answers one question about an
|
|
17
16
|
observation; `@disclosureos/schema` binds the package-owned pieces into one portable
|
|
18
17
|
contract (a single TS type + JSON Schema + non-stripping parse).
|
|
19
18
|
|
|
@@ -24,6 +23,7 @@ contract (a single TS type + JSON Schema + non-stripping parse).
|
|
|
24
23
|
| **Origins** | `@disclosureos/origins` | *What might explain it?* |
|
|
25
24
|
| **Claims** | `@disclosureos/records/shared` | *Who assessed it, why, and on what evidence?* |
|
|
26
25
|
| **Scoring** | `@disclosureos/scoring` | *How complete / compelling is the case?* |
|
|
26
|
+
| **Instruments** | `@disclosureos/instruments` | *What hardware produced the data?* |
|
|
27
27
|
|
|
28
28
|
Supporting packages: **`@disclosureos/schema`** (the portable contract that composes the
|
|
29
29
|
package-owned pieces), **`@disclosureos/cli`** (tooling), and **`@disclosureos/examples`**
|
|
@@ -33,8 +33,9 @@ package-owned pieces), **`@disclosureos/cli`** (tooling), and **`@disclosureos/e
|
|
|
33
33
|
|
|
34
34
|
Unlike `observables` and `origins`, scoring **only consumes** the foundation — it
|
|
35
35
|
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
|
|
36
|
+
depends on `@disclosureos/records`, `@disclosureos/observables`,
|
|
37
|
+
`@disclosureos/origins`, and `@disclosureos/instruments`, so importing it gives
|
|
38
|
+
you those slots typed automatically.
|
|
38
39
|
|
|
39
40
|
> **See it end to end.** [`examples/golden-path.ts`](../../examples/golden-path.ts)
|
|
40
41
|
> takes one observation through every layer — records → observables → origins → scoring,
|
|
@@ -52,13 +53,16 @@ pnpm add @disclosureos/scoring @disclosureos/records @disclosureos/observables @
|
|
|
52
53
|
|
|
53
54
|
## Quick start
|
|
54
55
|
|
|
56
|
+
The `getPopulationCoverage` name below requires this unreleased checkout or a future
|
|
57
|
+
authorized release containing it. Published consumers can continue using `getCompleteness`.
|
|
58
|
+
|
|
55
59
|
```typescript
|
|
56
|
-
import { score,
|
|
60
|
+
import { score, getPopulationCoverage, rankByCompellingness } from '@disclosureos/scoring';
|
|
57
61
|
|
|
58
62
|
const result = score(observation);
|
|
59
63
|
// { score, range: { low, high }, contested, ... }
|
|
60
64
|
|
|
61
|
-
const
|
|
65
|
+
const coverage = getPopulationCoverage(observation);
|
|
62
66
|
// { percentage, requiredPercentage, missing, ... }
|
|
63
67
|
|
|
64
68
|
const ranked = rankByCompellingness([obsA, obsB, obsC]); // most-compelling first
|
|
@@ -68,12 +72,35 @@ Because evaluative slots hold **arrays of competing claims**, `score()` reports
|
|
|
68
72
|
`range` (the spread across claims) and a `contested` flag when evaluators disagree —
|
|
69
73
|
not just a single point estimate.
|
|
70
74
|
|
|
75
|
+
## Instrument trust (calibration provenance)
|
|
76
|
+
|
|
77
|
+
`calibrationTrust` turns published sensor manifests
|
|
78
|
+
([`@disclosureos/instruments`](../disclosureos-instruments)) into a scoring
|
|
79
|
+
input. It builds an `evaluatorWeight` policy that credits claims whose sensor
|
|
80
|
+
evidence resolves — via `SensorReading.sensorRef` — to a manifest entry with
|
|
81
|
+
real calibration provenance (`in_practice` / `documented` earn the most; see
|
|
82
|
+
`CALIBRATION_TRUST_WEIGHTS`).
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { score, calibrationTrust } from '@disclosureos/scoring';
|
|
86
|
+
|
|
87
|
+
const result = score(observation, {
|
|
88
|
+
evaluatorWeight: calibrationTrust(observation, [navyManifest]),
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The default gradient is deliberately conservative: unresolved sensors keep the
|
|
93
|
+
1.0 baseline (records that predate manifests are never penalized), and like
|
|
94
|
+
every `evaluatorWeight` policy it shifts only the consensus point — `range` and
|
|
95
|
+
`contested` still report the honest spread of what evaluators asserted.
|
|
96
|
+
|
|
71
97
|
## Subpath exports
|
|
72
98
|
|
|
73
99
|
| Subpath | Contents |
|
|
74
100
|
|---|---|
|
|
75
|
-
| `@disclosureos/scoring` | `score`, `rankByCompellingness`, `getCompleteness`, `DEFAULT_WEIGHTS` |
|
|
76
|
-
| `@disclosureos/scoring/
|
|
101
|
+
| `@disclosureos/scoring` | `score`, `rankByCompellingness`, `getPopulationCoverage`, legacy `getCompleteness`, `DEFAULT_WEIGHTS` |
|
|
102
|
+
| `@disclosureos/scoring/population-coverage` | Field-presence counter and `deriveFieldPaths` |
|
|
103
|
+
| `@disclosureos/scoring/completeness` | Legacy field-presence counter + `deriveFieldPaths` |
|
|
77
104
|
| `@disclosureos/scoring/compellingness` | Compellingness scoring + weights |
|
|
78
105
|
| `@disclosureos/scoring/schema` | The committed JSON Schema (`scoring.schema.json`) |
|
|
79
106
|
|
|
@@ -87,3 +114,15 @@ them directly, with no DisclosureOS-specific adapter.
|
|
|
87
114
|
## License
|
|
88
115
|
|
|
89
116
|
MIT © [Disclosure Foundation](https://disclosure.org)
|
|
117
|
+
|
|
118
|
+
## Experimental v2 assessment summaries (unreleased)
|
|
119
|
+
|
|
120
|
+
`summarizeClaimHistory` from `@disclosureos/scoring/experimental/v2` groups current
|
|
121
|
+
attributed declarations by topic and subject, collapses exact copies, and discloses
|
|
122
|
+
shared inputs and differing outcome labels. It computes no aggregate score or
|
|
123
|
+
confidence interval and does not certify scientific eligibility. See the
|
|
124
|
+
[assessment summary guide](../../docs/experimental/assessment-summary.md).
|
|
125
|
+
|
|
126
|
+
The additive population-coverage names are unreleased. See the [coverage guide](../../docs/experimental/population-coverage.md) for retained counting rules, denominator limits and compatibility. The existing compellingness methodology is separate from experimental v2 assessment summaries and profile completion.
|
|
127
|
+
|
|
128
|
+
[Assessment-summary provenance and replay](../../docs/experimental/summary-provenance.md) now pin the declared history and computed summary without verifying referenced source artifacts.
|
|
@@ -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"]}
|
|
@@ -32,9 +32,9 @@ interface CompletenessOptions {
|
|
|
32
32
|
paths?: FieldPath[];
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* Count populated legacy schema field paths. A present value is not necessarily
|
|
36
|
+
* valid, documented, applicable or independently analyzable. Prefer the public
|
|
37
|
+
* getPopulationCoverage name for new callers; legacy behavior remains unchanged.
|
|
38
38
|
*/
|
|
39
39
|
declare function getCompleteness(observation: Observation | Record<string, unknown>, options?: CompletenessOptions): CompletenessResult;
|
|
40
40
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/completeness/field-paths.ts","../../src/completeness/index.ts"],"names":[],"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"]}
|
|
1
|
+
{"version":3,"sources":["../../src/completeness/field-paths.ts","../../src/completeness/index.ts"],"names":[],"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 * Count populated legacy schema field paths. A present value is not necessarily\n * valid, documented, applicable or independently analyzable. Prefer the public\n * getPopulationCoverage name for new callers; legacy behavior remains unchanged.\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"]}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { ClaimHistoryValidation, ClaimSubject, HistoricalClaim, SourceStatement, UnassessedClaim, AssessedClaim } from '@disclosureos/records/experimental/v2';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
declare const ASSESSMENT_SUMMARY_POLICY: Readonly<{
|
|
5
|
+
readonly id: "urn:disclosureos:experimental:policy:assessment-summary";
|
|
6
|
+
readonly version: "0.1.0";
|
|
7
|
+
readonly scope: "current_declared_assessments";
|
|
8
|
+
}>;
|
|
9
|
+
type Declaration<T extends HistoricalClaim> = Omit<T, "id" | "recordedAt" | "supersedes">;
|
|
10
|
+
interface DeclarationGroup<T extends HistoricalClaim> {
|
|
11
|
+
/** All current copies; the first sorted ref is the deterministic representative. */
|
|
12
|
+
claimRefs: string[];
|
|
13
|
+
declaration: Declaration<T>;
|
|
14
|
+
}
|
|
15
|
+
interface AssessmentSummaryGroup {
|
|
16
|
+
topic: string;
|
|
17
|
+
subject: ClaimSubject;
|
|
18
|
+
sourceAssertions: DeclarationGroup<SourceStatement>[];
|
|
19
|
+
unassessed: DeclarationGroup<UnassessedClaim>[];
|
|
20
|
+
assessments: (DeclarationGroup<AssessedClaim> & {
|
|
21
|
+
lineageRefs: string[];
|
|
22
|
+
})[];
|
|
23
|
+
declaredOutcomes: AssessedClaim["outcome"][];
|
|
24
|
+
disagreement: "not_assessed" | "single_declared_outcome" | "multiple_declared_outcomes";
|
|
25
|
+
}
|
|
26
|
+
interface AssessmentSummaryResult {
|
|
27
|
+
success: boolean;
|
|
28
|
+
policy: typeof ASSESSMENT_SUMMARY_POLICY;
|
|
29
|
+
contract: ClaimHistoryValidation["contract"];
|
|
30
|
+
checks: ClaimHistoryValidation["checks"];
|
|
31
|
+
issues: ClaimHistoryValidation["issues"];
|
|
32
|
+
uncheckedRefs: string[];
|
|
33
|
+
historyId?: string;
|
|
34
|
+
observationId?: string;
|
|
35
|
+
currentClaimRefs: string[];
|
|
36
|
+
supersededClaimRefs: string[];
|
|
37
|
+
groups: AssessmentSummaryGroup[];
|
|
38
|
+
sharedInputs: {
|
|
39
|
+
ref: string;
|
|
40
|
+
assessmentRefs: string[];
|
|
41
|
+
}[];
|
|
42
|
+
/** Equal declared hashes are a dependence signal, not a completed byte check. */
|
|
43
|
+
sharedDeclaredArtifacts: {
|
|
44
|
+
sha256: string;
|
|
45
|
+
artifactRefs: string[];
|
|
46
|
+
assessmentRefs: string[];
|
|
47
|
+
}[];
|
|
48
|
+
scientificEligibility: "not_checked";
|
|
49
|
+
artifactIntegrity: "not_checked";
|
|
50
|
+
vocabularyMembership: "not_checked";
|
|
51
|
+
statisticalIndependence: "not_checked";
|
|
52
|
+
reproducibility: "not_checked";
|
|
53
|
+
}
|
|
54
|
+
/** Summarize attributed declarations. No outcome is upgraded by a parser pass. */
|
|
55
|
+
declare function summarizeClaimHistory(input: unknown): AssessmentSummaryResult;
|
|
56
|
+
|
|
57
|
+
declare const ASSESSMENT_SUMMARY_EVALUATION_SCHEMA_ID = "urn:disclosureos:experimental:assessment-summary-evaluation:0.1.0";
|
|
58
|
+
declare const ASSESSMENT_SUMMARY_EVALUATION_POLICY: Readonly<{
|
|
59
|
+
readonly id: "urn:disclosureos:experimental:policy:assessment-summary-evaluation";
|
|
60
|
+
readonly version: "0.1.0";
|
|
61
|
+
readonly scope: "pinned_claim_history_and_computed_declarations";
|
|
62
|
+
}>;
|
|
63
|
+
declare const AssessmentSummaryEvaluationSchema: z.ZodObject<{
|
|
64
|
+
kind: z.ZodLiteral<"assessment_summary_evaluation">;
|
|
65
|
+
schemaVersion: z.ZodLiteral<"0.1.0">;
|
|
66
|
+
id: z.ZodString;
|
|
67
|
+
workflow: z.ZodLiteral<"assessment-summary:0.1.0">;
|
|
68
|
+
history: z.ZodObject<{
|
|
69
|
+
sha256: z.ZodString;
|
|
70
|
+
byteLength: z.ZodNumber;
|
|
71
|
+
}, z.core.$strict>;
|
|
72
|
+
dependencies: z.ZodObject<{
|
|
73
|
+
vocabularies: z.ZodArray<z.ZodObject<{
|
|
74
|
+
sha256: z.ZodString;
|
|
75
|
+
byteLength: z.ZodNumber;
|
|
76
|
+
ref: z.ZodString;
|
|
77
|
+
id: z.ZodString;
|
|
78
|
+
version: z.ZodString;
|
|
79
|
+
}, z.core.$strict>>;
|
|
80
|
+
implementation: z.ZodObject<{
|
|
81
|
+
sha256: z.ZodString;
|
|
82
|
+
byteLength: z.ZodNumber;
|
|
83
|
+
ref: z.ZodString;
|
|
84
|
+
id: z.ZodString;
|
|
85
|
+
version: z.ZodString;
|
|
86
|
+
}, z.core.$strict>;
|
|
87
|
+
environment: z.ZodObject<{
|
|
88
|
+
sha256: z.ZodString;
|
|
89
|
+
byteLength: z.ZodNumber;
|
|
90
|
+
ref: z.ZodString;
|
|
91
|
+
id: z.ZodString;
|
|
92
|
+
version: z.ZodString;
|
|
93
|
+
}, z.core.$strict>;
|
|
94
|
+
}, z.core.$strict>;
|
|
95
|
+
}, z.core.$strict>;
|
|
96
|
+
type AssessmentSummaryEvaluation = z.infer<typeof AssessmentSummaryEvaluationSchema>;
|
|
97
|
+
declare function assessmentSummaryEvaluationJsonSchema(): Record<string, unknown>;
|
|
98
|
+
interface AssessmentSummaryEvaluationOptions {
|
|
99
|
+
historyBytes?: Uint8Array;
|
|
100
|
+
dependencyFiles?: ReadonlyMap<string, Uint8Array>;
|
|
101
|
+
}
|
|
102
|
+
type Status = "passed" | "failed" | "not_checked";
|
|
103
|
+
interface AssessmentSummaryReceipt {
|
|
104
|
+
format: "disclosureos-assessment-summary-receipt:0.1.0";
|
|
105
|
+
serialization: "sorted-json-utf8:0.1.0";
|
|
106
|
+
request: {
|
|
107
|
+
sha256: string;
|
|
108
|
+
byteLength: number;
|
|
109
|
+
};
|
|
110
|
+
output: {
|
|
111
|
+
sha256: string;
|
|
112
|
+
byteLength: number;
|
|
113
|
+
};
|
|
114
|
+
requestJson: string;
|
|
115
|
+
outputJson: string;
|
|
116
|
+
}
|
|
117
|
+
interface AssessmentSummaryEvaluationResult {
|
|
118
|
+
success: boolean;
|
|
119
|
+
policy: typeof ASSESSMENT_SUMMARY_EVALUATION_POLICY;
|
|
120
|
+
checks: {
|
|
121
|
+
structural: Status;
|
|
122
|
+
semantic: Status;
|
|
123
|
+
external: Status;
|
|
124
|
+
summary: Status;
|
|
125
|
+
receipt: Status;
|
|
126
|
+
};
|
|
127
|
+
issues: {
|
|
128
|
+
code: string;
|
|
129
|
+
pointer: string;
|
|
130
|
+
message: string;
|
|
131
|
+
}[];
|
|
132
|
+
files: {
|
|
133
|
+
ref: string;
|
|
134
|
+
status: Status;
|
|
135
|
+
expectedSha256: string;
|
|
136
|
+
expectedByteLength: number;
|
|
137
|
+
actualSha256?: string;
|
|
138
|
+
actualByteLength?: number;
|
|
139
|
+
}[];
|
|
140
|
+
summary: AssessmentSummaryResult | null;
|
|
141
|
+
receipt: AssessmentSummaryReceipt | null;
|
|
142
|
+
sourceArtifactIntegrity: "not_checked";
|
|
143
|
+
vocabularyMembership: "not_checked";
|
|
144
|
+
scientificEligibility: "not_checked";
|
|
145
|
+
statisticalIndependence: "not_checked";
|
|
146
|
+
implementationExecution: "not_attested";
|
|
147
|
+
environmentExecution: "not_attested";
|
|
148
|
+
}
|
|
149
|
+
/** Pins the summary's declared history input. Referenced source/product bytes are not verified or fetched. */
|
|
150
|
+
declare function evaluateAssessmentSummaryEvaluation(input: unknown, options?: AssessmentSummaryEvaluationOptions): Promise<AssessmentSummaryEvaluationResult>;
|
|
151
|
+
|
|
152
|
+
export { ASSESSMENT_SUMMARY_EVALUATION_POLICY, ASSESSMENT_SUMMARY_EVALUATION_SCHEMA_ID, ASSESSMENT_SUMMARY_POLICY, type AssessmentSummaryEvaluation, type AssessmentSummaryEvaluationOptions, type AssessmentSummaryEvaluationResult, AssessmentSummaryEvaluationSchema, type AssessmentSummaryGroup, type AssessmentSummaryReceipt, type AssessmentSummaryResult, type DeclarationGroup, assessmentSummaryEvaluationJsonSchema, evaluateAssessmentSummaryEvaluation, summarizeClaimHistory };
|