@kontourai/survey 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,6 +64,60 @@ const trustInput = validateTrustInput(buildSurveyTrustInput(surveyInput));
64
64
  const report = buildTrustReport(trustInput);
65
65
  ```
66
66
 
67
+ ## Raw sources
68
+
69
+ Use raw-source helpers when a producer wants Survey to shape source identity
70
+ before building observations. The helpers do not fetch, crawl, parse, or judge
71
+ the source; they only produce stable `RawSource` records with explicit source
72
+ references, observed times, locator schemes, checksums, and producer metadata.
73
+
74
+ ```ts
75
+ import {
76
+ apiRecordSource,
77
+ fieldObservation,
78
+ SurveyInputBuilder,
79
+ } from "@kontourai/survey";
80
+
81
+ const rawSource = apiRecordSource({
82
+ sourceRef: "example-records://entity/entity-123",
83
+ observedAt: new Date().toISOString(),
84
+ checksum: "abc123",
85
+ metadata: {
86
+ provider: "example-records",
87
+ },
88
+ });
89
+
90
+ const surveyInput = new SurveyInputBuilder({ source: "example-producer:run-1" })
91
+ .addObservation(fieldObservation({
92
+ id: "entity-123.status.current",
93
+ field: "registrationStatus",
94
+ value: "ACTIVE",
95
+ rawSource,
96
+ extraction: {
97
+ confidence: 0.97,
98
+ locator: "json:$.registrationStatus",
99
+ extractor: "example-extractor",
100
+ extractedAt: new Date().toISOString(),
101
+ },
102
+ claim: {
103
+ subjectType: "public-record.entity",
104
+ subjectId: "entity-123",
105
+ surface: "example.profile",
106
+ claimType: "public-data.field",
107
+ status: "proposed",
108
+ impactLevel: "medium",
109
+ collectedBy: "example-extractor",
110
+ },
111
+ }))
112
+ .build();
113
+ ```
114
+
115
+ Survey exports `uploadedDocumentSource`, `apiRecordSource`, `webPageSource`,
116
+ and `manualEntrySource`. Producer-provided `id` values are preserved; otherwise
117
+ Survey derives a stable id from source kind and `sourceRef`. Bare checksum
118
+ values are normalized to `sha256:<value>`, while already-prefixed checksum
119
+ values are preserved. Producer metadata is copied through to Surface evidence.
120
+
67
121
  ## Field observations
68
122
 
69
123
  Use `fieldObservation` when a producer wants to describe one scalar field value
@@ -129,6 +183,20 @@ preserved. Producers still own scalar semantics, validation, candidate ranking,
129
183
  review policy, and whether a value should be verified, proposed, rejected, or
130
184
  assumed.
131
185
 
186
+ ## Reviewed candidate resolutions
187
+
188
+ Use `reviewedCandidateResolution` when a producer has multiple candidate
189
+ observations for the same target and a review outcome selects one candidate.
190
+ The helper wraps `candidateReviewRecord`, attaches the review outcome to the
191
+ selected candidate, defaults the candidate set to `resolved`, defaults the
192
+ selected claim status from the review outcome, and defaults unselected
193
+ candidates to `superseded`. Producers can override selected or unselected claim
194
+ statuses when their domain workflow needs a different posture.
195
+
196
+ This is useful for corrected documents, source-of-truth choices, and review
197
+ queues where losing candidates should remain visible for transparency rather
198
+ than disappearing from the trust trail.
199
+
132
200
  ## Repeated observations
133
201
 
134
202
  Use `repeatedObservation` when a producer wants to describe a repeated field or
@@ -1,8 +1,12 @@
1
1
  export type { CandidateSetStatus, Candidate, CandidateSet, ClaimTarget, Extraction, LocatorScheme, RawSource, RawSourceKind, ReviewOutcome, ReviewStatus, SurveyInput, } from "./types.js";
2
2
  export { candidateReviewRecord, SurveyInputBuilder } from "./builder.js";
3
3
  export type { CandidateReviewRecordInput, SurveyClaimRecord, SurveyInputBuilderArgs, SurveyObservationInput, } from "./builder.js";
4
+ export { reviewedCandidateResolution } from "./reviewed-candidate-resolution.js";
5
+ export type { ReviewedCandidateResolutionInput } from "./reviewed-candidate-resolution.js";
4
6
  export { buildSurveyTrustInput } from "./to-surface.js";
5
7
  export { fieldObservation } from "./field-observation.js";
6
8
  export type { FieldObservationInput } from "./field-observation.js";
7
9
  export { repeatedObservation } from "./repeated-observation.js";
8
10
  export type { RepeatedObservationInput } from "./repeated-observation.js";
11
+ export { apiRecordSource, manualEntrySource, uploadedDocumentSource, webPageSource, } from "./raw-source.js";
12
+ export type { ApiRecordSourceInput, ChecksumInput, ManualEntrySourceInput, RawSourceInput, UploadedDocumentSourceInput, WebPageSourceInput, } from "./raw-source.js";
package/dist/src/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  export { candidateReviewRecord, SurveyInputBuilder } from "./builder.js";
2
+ export { reviewedCandidateResolution } from "./reviewed-candidate-resolution.js";
2
3
  export { buildSurveyTrustInput } from "./to-surface.js";
3
4
  export { fieldObservation } from "./field-observation.js";
4
5
  export { repeatedObservation } from "./repeated-observation.js";
6
+ export { apiRecordSource, manualEntrySource, uploadedDocumentSource, webPageSource, } from "./raw-source.js";
@@ -0,0 +1,31 @@
1
+ import type { LocatorScheme, RawSource } from "./types.js";
2
+ export type ChecksumInput = string | {
3
+ algorithm?: string;
4
+ value: string;
5
+ };
6
+ export interface RawSourceInput {
7
+ id?: string;
8
+ sourceRef: string;
9
+ observedAt: string;
10
+ fetchedAt?: string;
11
+ checksum?: ChecksumInput;
12
+ locatorScheme: LocatorScheme;
13
+ metadata?: Record<string, unknown>;
14
+ }
15
+ export interface UploadedDocumentSourceInput extends RawSourceInput {
16
+ locatorScheme: LocatorScheme;
17
+ }
18
+ export interface ApiRecordSourceInput extends Omit<RawSourceInput, "locatorScheme"> {
19
+ locatorScheme?: LocatorScheme;
20
+ }
21
+ export interface WebPageSourceInput extends Omit<RawSourceInput, "locatorScheme"> {
22
+ locatorScheme?: LocatorScheme;
23
+ }
24
+ export interface ManualEntrySourceInput extends Omit<RawSourceInput, "checksum" | "locatorScheme"> {
25
+ checksum?: ChecksumInput;
26
+ locatorScheme?: LocatorScheme;
27
+ }
28
+ export declare function uploadedDocumentSource(input: UploadedDocumentSourceInput): RawSource;
29
+ export declare function apiRecordSource(input: ApiRecordSourceInput): RawSource;
30
+ export declare function webPageSource(input: WebPageSourceInput): RawSource;
31
+ export declare function manualEntrySource(input: ManualEntrySourceInput): RawSource;
@@ -0,0 +1,48 @@
1
+ export function uploadedDocumentSource(input) {
2
+ return rawSource("uploaded-document", input);
3
+ }
4
+ export function apiRecordSource(input) {
5
+ return rawSource("api-record", {
6
+ locatorScheme: "structured-field",
7
+ ...input,
8
+ });
9
+ }
10
+ export function webPageSource(input) {
11
+ return rawSource("web-page", {
12
+ locatorScheme: "html",
13
+ ...input,
14
+ });
15
+ }
16
+ export function manualEntrySource(input) {
17
+ return rawSource("manual-entry", {
18
+ locatorScheme: "structured-field",
19
+ ...input,
20
+ });
21
+ }
22
+ function rawSource(kind, input) {
23
+ return {
24
+ id: input.id ?? sourceId(kind, input.sourceRef),
25
+ kind,
26
+ sourceRef: input.sourceRef,
27
+ observedAt: input.observedAt,
28
+ fetchedAt: input.fetchedAt,
29
+ checksum: normalizeChecksum(input.checksum),
30
+ locatorScheme: input.locatorScheme,
31
+ metadata: input.metadata,
32
+ };
33
+ }
34
+ function sourceId(kind, sourceRef) {
35
+ return `${kind}:${sourceRef}`;
36
+ }
37
+ function normalizeChecksum(checksum) {
38
+ if (!checksum)
39
+ return undefined;
40
+ if (typeof checksum === "string") {
41
+ if (checksum.includes(":"))
42
+ return checksum;
43
+ return `sha256:${checksum}`;
44
+ }
45
+ if (checksum.value.includes(":"))
46
+ return checksum.value;
47
+ return `${checksum.algorithm ?? "sha256"}:${checksum.value}`;
48
+ }
@@ -0,0 +1,18 @@
1
+ import { type SurveyClaimRecord, type SurveyObservationInput } from "./builder.js";
2
+ import type { CandidateSet, ClaimTarget, ReviewOutcome } from "./types.js";
3
+ export interface ReviewedCandidateResolutionInput {
4
+ id: string;
5
+ target: string;
6
+ observations: SurveyObservationInput[];
7
+ selectedCandidateId: string;
8
+ rationale?: string;
9
+ metadata?: Record<string, unknown>;
10
+ status?: CandidateSet["status"];
11
+ reviewOutcome: Omit<ReviewOutcome, "id" | "candidateSetId" | "candidateId"> & {
12
+ id?: string;
13
+ candidateId?: string;
14
+ };
15
+ selectedClaimStatus?: ClaimTarget["status"];
16
+ unselectedClaimStatus?: ClaimTarget["status"];
17
+ }
18
+ export declare function reviewedCandidateResolution(input: ReviewedCandidateResolutionInput): SurveyClaimRecord[];
@@ -0,0 +1,36 @@
1
+ import { candidateReviewRecord } from "./builder.js";
2
+ export function reviewedCandidateResolution(input) {
3
+ return candidateReviewRecord({
4
+ id: input.id,
5
+ target: input.target,
6
+ selectedCandidateId: input.selectedCandidateId,
7
+ status: input.status ?? candidateSetStatusForReview(input.reviewOutcome.status),
8
+ rationale: input.rationale,
9
+ metadata: input.metadata,
10
+ reviewOutcome: {
11
+ ...input.reviewOutcome,
12
+ candidateId: input.reviewOutcome.candidateId ?? input.selectedCandidateId,
13
+ },
14
+ observations: input.observations.map((observation) => ({
15
+ ...observation,
16
+ claim: {
17
+ ...observation.claim,
18
+ status: observation.claim.status ?? claimStatusForObservation(input, observation),
19
+ },
20
+ })),
21
+ });
22
+ }
23
+ function claimStatusForObservation(input, observation) {
24
+ if (observationCandidateId(observation) === input.selectedCandidateId) {
25
+ return input.selectedClaimStatus ?? input.reviewOutcome.status;
26
+ }
27
+ return input.unselectedClaimStatus ?? "superseded";
28
+ }
29
+ function observationCandidateId(observation) {
30
+ return observation.candidate?.id ?? `${observation.id}.candidate`;
31
+ }
32
+ function candidateSetStatusForReview(status) {
33
+ if (status === "proposed")
34
+ return "needs-review";
35
+ return "resolved";
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontourai/survey",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Producer-side source, extraction, candidate, and review contracts for projecting verified claims into Surface.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",