@kontourai/survey 4.0.0 → 6.0.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/dist/examples/review-workbench/server-apply-consumer.js +5 -0
- package/dist/src/canonical-reviewed-trust-input.js +73 -34
- package/dist/src/extraction-envelope.d.ts +125 -3
- package/dist/src/extraction-envelope.js +332 -25
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +1 -1
- package/dist/src/mcp/review-mcp.js +62 -25
- package/dist/src/review-workbench/audit-rows.d.ts +1 -1
- package/dist/src/review-workbench/audit-rows.js +1 -0
- package/dist/src/review-workbench/extraction-inspector.d.ts +31 -13
- package/dist/src/review-workbench/extraction-inspector.js +77 -47
- package/dist/src/review-workbench/queue-binding.js +1 -1
- package/dist/src/review-workbench/review-presentation.d.ts +26 -1
- package/dist/src/review-workbench/review-presentation.js +75 -1
- package/dist/src/review-workbench/review-queue-session.d.ts +19 -5
- package/dist/src/review-workbench/review-queue-session.js +46 -13
- package/dist/src/review-workbench/review-session-replay.js +5 -3
- package/dist/src/review-workbench/review-workbench-css.generated.js +10 -1
- package/dist/src/review-workbench/review-workbench.css +10 -1
- package/dist/src/review-workbench/review-workbench.d.ts +14 -7
- package/dist/src/review-workbench/review-workbench.js +111 -20
- package/dist/src/review-workbench/review-workbench.standalone.css +10 -1
- package/dist/src/surface-reviewed-extraction.js +4 -0
- package/dist/src/to-surface.js +116 -0
- package/dist/src/types.d.ts +5 -1
- package/package.json +1 -1
package/dist/src/to-surface.js
CHANGED
|
@@ -44,6 +44,10 @@ export function buildSurveyTrustBundle(input, options = {}) {
|
|
|
44
44
|
const events = [];
|
|
45
45
|
for (const projection of input.claims) {
|
|
46
46
|
const candidateSet = requireMapValue(candidateSets, projection.candidateSetId, "candidate set");
|
|
47
|
+
if (projection.candidateId === undefined && candidateSet.selectedCandidateId === undefined && candidateSet.candidates.length > 1) {
|
|
48
|
+
projectUnselectedSetClaim({ input, projection, candidateSet, rawSources, extractions, reviewsByCandidateSet, projectionContextId, claims, evidence, events });
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
47
51
|
const candidate = selectCandidate(candidateSet, projection.candidateId);
|
|
48
52
|
const extraction = requireMapValue(extractions, candidate.extractionId, "extraction");
|
|
49
53
|
const rawSource = requireMapValue(rawSources, extraction.sourceId, "raw source");
|
|
@@ -220,6 +224,116 @@ export function buildSurveyTrustBundle(input, options = {}) {
|
|
|
220
224
|
events,
|
|
221
225
|
};
|
|
222
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* A claim over a candidate set that selects none of its candidates (a review
|
|
229
|
+
* rejected every conflicting value, or could not confirm any). No single value
|
|
230
|
+
* is presented: the claim value is the projection's own (null from the
|
|
231
|
+
* canonical path), every candidate is listed in `metadata.survey.candidates`
|
|
232
|
+
* and backed by its own evidence record, and the status can never be trusted.
|
|
233
|
+
* A review that names one candidate cannot apply to such a claim and is
|
|
234
|
+
* refused rather than dropped. With `reviewProofs`, no integrity anchor is
|
|
235
|
+
* attached: the anchor commits one reviewed candidate, and this claim has none
|
|
236
|
+
* (it is never `verified` or `assumed`).
|
|
237
|
+
*/
|
|
238
|
+
function projectUnselectedSetClaim(context) {
|
|
239
|
+
const { input, projection, candidateSet, projectionContextId } = context;
|
|
240
|
+
const setReviews = context.reviewsByCandidateSet.get(candidateSet.id) ?? [];
|
|
241
|
+
const candidateReviews = setReviews.filter((review) => review.candidateId);
|
|
242
|
+
if (candidateReviews.length) {
|
|
243
|
+
throw new Error(`Claim ${projection.id} names no candidate of set ${candidateSet.id}, but review ${candidateReviews.map((review) => review.id).join(", ")} is about candidate ${candidateReviews.map((review) => review.candidateId).join(", ")}: a candidate-level review needs a selectedCandidateId on the set or a candidateId on the claim.`);
|
|
244
|
+
}
|
|
245
|
+
const reviews = setReviews;
|
|
246
|
+
const review = latestReview(reviews, projection.id);
|
|
247
|
+
const setStatus = statusFor({ candidateSet, candidate: { id: "", extractionId: "", value: null } });
|
|
248
|
+
const status = projection.status
|
|
249
|
+
?? (review?.resolution === "could_not_confirm"
|
|
250
|
+
? (setStatus === "disputed" ? setStatus : review.status)
|
|
251
|
+
: review?.status ?? setStatus);
|
|
252
|
+
if (status === "verified" || status === "assumed") {
|
|
253
|
+
throw new ReviewAgreementError("status-mismatch", `Claim ${projection.id} selects no candidate of set ${candidateSet.id}, so it cannot be ${status}`);
|
|
254
|
+
}
|
|
255
|
+
assertReviewOutcomeDiscipline({ subject: `Claim ${projection.id}`, status, review, candidateSetStatus: candidateSet.status });
|
|
256
|
+
const sources = candidateSet.candidates.map((candidate) => {
|
|
257
|
+
const extraction = requireMapValue(context.extractions, candidate.extractionId, "extraction");
|
|
258
|
+
const rawSource = requireMapValue(context.rawSources, extraction.sourceId, "raw source");
|
|
259
|
+
return { candidate, extraction, rawSource };
|
|
260
|
+
});
|
|
261
|
+
const evidenceIds = sources.map(({ candidate, extraction, rawSource }) => {
|
|
262
|
+
const id = projectionRecordId(projection.id, projectionContextId, "claim-evidence", `evidence.source.${candidate.id}`);
|
|
263
|
+
const policyStandard = policyStandardFields(rawSource);
|
|
264
|
+
context.evidence.push({
|
|
265
|
+
id,
|
|
266
|
+
claimId: projection.id,
|
|
267
|
+
evidenceType: projection.evidenceType ?? (rawSource.resolution ? evidenceTypeForResolution(rawSource, rawSource.resolution) : evidenceTypeFor(rawSource)),
|
|
268
|
+
method: projection.evidenceMethod ?? "extraction",
|
|
269
|
+
sourceRef: rawSource.sourceRef,
|
|
270
|
+
sourceLocator: extraction.locator,
|
|
271
|
+
excerptOrSummary: evidenceExcerptOrSummary({ rawSource, extraction, projection, policyStandard }),
|
|
272
|
+
observedAt: rawSource.observedAt,
|
|
273
|
+
collectedBy: projection.collectedBy,
|
|
274
|
+
integrityRef: rawSource.checksum,
|
|
275
|
+
metadata: {
|
|
276
|
+
...rawSource.metadata,
|
|
277
|
+
...extraction.metadata,
|
|
278
|
+
...candidate.metadata,
|
|
279
|
+
...(policyStandard ? { policyStandard } : {}),
|
|
280
|
+
rawSourceKind: rawSource.kind,
|
|
281
|
+
locatorScheme: rawSource.locatorScheme,
|
|
282
|
+
candidateId: candidate.id,
|
|
283
|
+
...(candidate.confidence ?? extraction.confidence) !== undefined ? { confidence: candidate.confidence ?? extraction.confidence } : {},
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
return id;
|
|
287
|
+
});
|
|
288
|
+
const producerSurvey = isRecord(projection.metadata?.survey) ? projection.metadata.survey : {};
|
|
289
|
+
context.claims.push({
|
|
290
|
+
id: projection.id,
|
|
291
|
+
subjectType: projection.subjectType,
|
|
292
|
+
subjectId: projection.subjectId,
|
|
293
|
+
facet: projection.facet,
|
|
294
|
+
claimType: projection.claimType,
|
|
295
|
+
fieldOrBehavior: projection.fieldOrBehavior,
|
|
296
|
+
value: "value" in projection ? projection.value : null,
|
|
297
|
+
status,
|
|
298
|
+
createdAt: projection.createdAt ?? sources.map(({ extraction }) => extraction.extractedAt).sort()[0],
|
|
299
|
+
updatedAt: projection.updatedAt ?? review?.reviewedAt ?? input.generatedAt,
|
|
300
|
+
impactLevel: projection.impactLevel,
|
|
301
|
+
derivedFrom: projection.derivedFrom,
|
|
302
|
+
derivationEdges: projection.derivationEdges,
|
|
303
|
+
confidenceBasis: {
|
|
304
|
+
sourceQuality: "moderate",
|
|
305
|
+
reviewerAuthority: "none",
|
|
306
|
+
evidenceStrength: "weak",
|
|
307
|
+
impactLevel: projection.impactLevel,
|
|
308
|
+
...projection.confidenceBasis,
|
|
309
|
+
},
|
|
310
|
+
metadata: {
|
|
311
|
+
...projection.metadata,
|
|
312
|
+
survey: {
|
|
313
|
+
...producerSurvey,
|
|
314
|
+
candidateSetId: candidateSet.id,
|
|
315
|
+
candidateSetStatus: candidateSet.status,
|
|
316
|
+
// No candidate is selected; every value the set held, in set order.
|
|
317
|
+
candidates: candidateSet.candidates.map((candidate) => ({
|
|
318
|
+
candidateId: candidate.id,
|
|
319
|
+
value: candidate.value,
|
|
320
|
+
...(candidate.rejectionReason !== undefined ? { rejectionReason: candidate.rejectionReason } : {}),
|
|
321
|
+
})),
|
|
322
|
+
reviewOutcomeId: review?.id,
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
context.events.push({
|
|
327
|
+
id: projectionRecordId(projection.id, projectionContextId, "claim-event", `event.${status}`),
|
|
328
|
+
claimId: projection.id,
|
|
329
|
+
status,
|
|
330
|
+
actor: projection.actor ?? (review?.resolution === "could_not_confirm" ? undefined : review?.actor) ?? projection.collectedBy,
|
|
331
|
+
method: projection.eventMethod ?? eventMethodFor(status, candidateSet),
|
|
332
|
+
evidenceIds,
|
|
333
|
+
createdAt: review?.reviewedAt ?? input.generatedAt,
|
|
334
|
+
notes: review?.rationale ?? candidateSet.rationale,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
223
337
|
function validateProjectionContextId(contextId) {
|
|
224
338
|
if (contextId === undefined)
|
|
225
339
|
return undefined;
|
|
@@ -504,6 +618,8 @@ function statusFor(input) {
|
|
|
504
618
|
return "disputed";
|
|
505
619
|
if (input.candidateSet.status === "escalated")
|
|
506
620
|
return "disputed";
|
|
621
|
+
if (input.candidateSet.status === "rejected")
|
|
622
|
+
return "rejected";
|
|
507
623
|
return "proposed";
|
|
508
624
|
}
|
|
509
625
|
function assertProducerDiscipline(input) {
|
package/dist/src/types.d.ts
CHANGED
|
@@ -28,7 +28,11 @@ export interface Extraction {
|
|
|
28
28
|
extractedAt: string;
|
|
29
29
|
metadata?: Record<string, unknown>;
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
* `rejected`: a review rejected every candidate in the set and selected none
|
|
33
|
+
* (for example rejecting all conflicting values for one claim).
|
|
34
|
+
*/
|
|
35
|
+
export type CandidateSetStatus = "resolved" | "needs-review" | "conflict" | "escalated" | "rejected";
|
|
32
36
|
export type EscalationDimension = "framing" | "completeness" | "conclusion" | "citation";
|
|
33
37
|
export interface Candidate {
|
|
34
38
|
id: string;
|
package/package.json
CHANGED