@kontourai/survey 0.4.23 → 0.4.24

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
@@ -1,24 +1,45 @@
1
+ <div align="center">
2
+
1
3
  # Kontour Survey
2
4
 
3
- Survey is the producer-side contract for turning producer observations into
4
- Surface-ready `TrustInput`.
5
+ **The producer side of trust. Survey carries evidence from raw source to reviewed claim — without ever pretending to know what's true.**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/%40kontourai%2Fsurvey)](https://www.npmjs.com/package/@kontourai/survey)
8
+ [![CI](https://github.com/kontourai/survey/actions/workflows/ci.yml/badge.svg)](https://github.com/kontourai/survey/actions/workflows/ci.yml)
9
+ [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
10
+
11
+ [Documentation](https://kontourai.github.io/survey/) · [Record Contracts](docs/record-contracts.md) · [Consumer Guide](docs/consumer-integration-guide.md) · [kontourai.io/survey](https://kontourai.io/survey)
12
+
13
+ </div>
14
+
15
+ ---
16
+
17
+ Every "verified" value in a data product has a story: where it was observed, what was extracted, what the alternatives were, who reviewed it, and what they decided. Most systems throw that story away the moment a human clicks approve — leaving a bare value nobody can re-inspect.
18
+
19
+ Survey is the contract that keeps the story. Producers own acquisition, parsing, ranking, review UX, and vertical policy; Survey owns the portable record shapes for the **source → extraction → candidate → review → claim** chain, and projects them into [Surface](https://kontourai.io/surface) `TrustInput` — so downstream trust reports, consoles, and [Flow](https://kontourai.github.io/flow/) gates can see not just the value, but the evidence and the review posture behind it.
20
+
21
+ Survey does not decide whether a real-world value is true. It preserves the producer's evidence and review discipline so something downstream can.
5
22
 
6
- This repo is intentionally small right now. It is a proof package, not an
7
- ingestion platform:
23
+ ## What you get
8
24
 
9
- - producers own acquisition, parsing, ranking, review UX, and vertical policy;
10
- - Survey owns source, extraction, candidate, and review record shapes;
11
- - `buildSurveyTrustInput` projects those records into `@kontourai/surface`
12
- `TrustInput`;
13
- - Surface owns Claim, Subject, Claim Type, Evidence, Status, Claim Dependency,
14
- TrustInput, trust reporting, and public reporting surfaces.
25
+ - **Typed record contracts** for raw sources, extractions, candidates, review outcomes, source-of-authority posture, repeated observations, resolutions, review proofs, and adversarial passes — every link in the evidence chain inspectable after the fact.
26
+ - **One projection to Surface.** `buildSurveyTrustInput` turns Survey records into Surface `TrustInput`; Surface owns claims, evidence, status, and trust reporting from there.
27
+ - **An embeddable Review Workbench** — a framework-neutral UI for working a `ReviewItem` queue: current vs proposed values, source refs and excerpts, decision controls, and a live Surface preview.
28
+ - **A server-owned apply boundary.** Review decisions are derived from pre-decision snapshots plus persisted events — never from browser-computed payloads — with freshness and replay checks built in.
29
+ - **Adversarial-pass records** for high-stakes review loops, designed to serve as per-round evidence for [Flow's adversarial route-back pattern](https://kontourai.github.io/flow/gates-and-route-back.html#pattern-adversarial-review-with-a-defect-budget).
15
30
 
16
- The first success criterion is that generic corrected-document and public-field
17
- fixtures can pass through Survey and produce valid Surface reports without
18
- Survey absorbing vertical policy.
31
+ ## See it
32
+
33
+ The Review Workbench rendering a real fixture queue — current vs proposed values, source evidence, decision effect, and the Surface projection preview:
34
+
35
+ ![Survey Review Workbench showing a review queue, current versus proposed values, decision controls, and a Surface preview](docs/assets/review-workbench-desktop.png)
19
36
 
20
37
  ## Quickstart
21
38
 
39
+ ```sh
40
+ npm install @kontourai/survey @kontourai/surface
41
+ ```
42
+
22
43
  ```ts
23
44
  import { buildTrustReport, validateTrustInput } from "@kontourai/surface";
24
45
  import { buildSurveyTrustInput, SurveyInputBuilder } from "@kontourai/survey";
@@ -64,10 +85,20 @@ const trustInput = validateTrustInput(buildSurveyTrustInput(surveyInput));
64
85
  const report = buildTrustReport(trustInput);
65
86
  ```
66
87
 
67
- ## Review Workbench Embed
88
+ One observation, one chain: the page it came from, what the extractor read, who verified it, and the claim it supports — projected into a Surface trust report with the evidence attached.
89
+
90
+ ## The producer validation path
68
91
 
69
- Survey also exposes a framework-neutral review workbench for downstream
70
- products that already produce `ReviewItem` queues.
92
+ 1. Build Survey observations with source, extraction, candidate, review, and claim records.
93
+ 2. Call `buildSurveyTrustInput` to project the Survey records into Surface `TrustInput`.
94
+ 3. Call Surface `validateTrustInput` on the projected input.
95
+ 4. Optionally call public Surface report APIs such as `buildTrustReport` to inspect claims, evidence, status, gaps, and metadata.
96
+
97
+ Keep producer operational state outside Survey. Queue status, reviewer form state, retries, source caches, and product policy decisions belong in the producer's own data model. Survey carries only the portable source, extraction, candidate, review, and claim projection records needed by Surface.
98
+
99
+ ## Review Workbench embed
100
+
101
+ For downstream products that already produce `ReviewItem` queues:
71
102
 
72
103
  ```ts
73
104
  import {
@@ -86,880 +117,63 @@ const presentationAdapter = {
86
117
  mountReviewWorkbench(element, reviewQueueSession, { presentationAdapter });
87
118
  ```
88
119
 
89
- The default stylesheet is scoped to `.survey-workbench-embed` and bundles the
90
- Console Kit tokens it needs, so importing it should not rewrite the host
91
- application's `body` or `:root` styles. Hosts should mount into an element like:
120
+ The stylesheet is scoped to `.survey-workbench-embed` and bundles the Console Kit tokens it needs, so it will not rewrite the host application's `body` or `:root` styles. Mount into:
92
121
 
93
122
  ```html
94
123
  <div class="survey-workbench-embed theme-survey"></div>
95
124
  ```
96
125
 
97
- The package also exposes `@kontourai/survey/review-workbench/standalone.css` for
98
- the standalone demo page. Use that only when Survey owns the whole page.
99
-
100
- For the full consumer path from `ReviewItem` construction through persisted
101
- review events, exported results, and optional Surface projection, see
102
- [`docs/consumer-integration-guide.md`](docs/consumer-integration-guide.md).
103
- That guide also covers the server-side apply boundary: producers should derive
104
- write results from pre-decision review snapshots plus persisted events, not from
105
- browser-computed decisions or exported result payloads.
106
- Use `persistReviewSessionEvents` when server code needs to save review events,
107
- then pass the persisted event set to `deriveReviewSessionApplyResultForSnapshot`
108
- before applying product policy. Survey derives selected review results and
109
- structured replay/completion issues; the producer still owns current-record
110
- validation and writes.
111
- For browser-backed queues, server code can import
112
- `@kontourai/survey/review-workbench/server-review-session` and use
113
- `createServerReviewSessionRecord`, `hashReviewSessionSnapshot`,
114
- `assertServerReviewSessionFreshness`, and `assertServerReviewSessionEvents` to
115
- keep the review snapshot server-owned while accepting browser-submitted
116
- `ReviewSessionEvent` resources. `deriveServerReviewSessionApplyResult` composes
117
- those checks with Survey's apply-result derivation for server-side write paths.
118
- For generic, test-covered consumer examples, see
119
- [`examples/review-workbench/facility-credential-consumer.ts`](examples/review-workbench/facility-credential-consumer.ts)
120
- for presentation and event persistence, and
121
- [`examples/review-workbench/server-apply-consumer.ts`](examples/review-workbench/server-apply-consumer.ts)
122
- for a compact server-side apply boundary.
123
- For the current decision on why Survey is not adding a generic review adapter
124
- builder yet, see
125
- [`docs/consumer-adapter-abstraction-assessment.md`](docs/consumer-adapter-abstraction-assessment.md).
126
-
127
- ## Contributor checks
128
-
129
- Install the repo-owned Git hooks once per clone:
130
-
131
- ```bash
132
- npm run setup:repo-hooks
133
- ```
134
-
135
- The setup command is idempotent. It sets this repo's local `core.hooksPath` to
136
- `.githooks` and does not require global Git configuration.
137
-
138
- Use the same checks directly when you want to validate hook drift or package
139
- health before pushing:
140
-
141
- ```bash
142
- npm run validate:repo-hooks
143
- npm run verify
144
- ```
145
-
146
- The committed pre-push hook runs both commands from the repo root.
147
-
148
- ## Producer validation path
149
-
150
- Survey producers validate through public `@kontourai/survey` and
151
- `@kontourai/surface` contracts:
152
-
153
- 1. Build Survey observations with source, extraction, candidate, review, and
154
- claim records.
155
- 2. Call `buildSurveyTrustInput` to project the Survey records into Surface
156
- `TrustInput`.
157
- 3. Call Surface `validateTrustInput` on the projected input.
158
- 4. Optionally call public Surface report APIs such as `buildTrustReport` to
159
- inspect claims, evidence, status, gaps, and metadata.
160
-
161
- Keep producer operational state outside Survey. Queue status, reviewer form
162
- state, retries, source caches, and product policy decisions belong in the
163
- producer's own data model. Survey carries only the portable source,
164
- extraction, candidate, review, and claim projection records needed by Surface.
165
-
166
- ## Raw sources
167
-
168
- Use raw-source helpers when a producer wants Survey to shape source identity
169
- before building observations. The helpers do not fetch, crawl, parse, or judge
170
- the source; they only produce stable `RawSource` records with explicit source
171
- references, observed times, locator schemes, checksums, and producer metadata.
172
-
173
- ```ts
174
- import {
175
- apiRecordSource,
176
- fieldObservation,
177
- SurveyInputBuilder,
178
- } from "@kontourai/survey";
179
-
180
- const rawSource = apiRecordSource({
181
- sourceRef: "example-records://entity/entity-123",
182
- observedAt: new Date().toISOString(),
183
- checksum: "abc123",
184
- metadata: {
185
- provider: "example-records",
186
- },
187
- });
188
-
189
- const surveyInput = new SurveyInputBuilder({ source: "example-producer:run-1" })
190
- .addObservation(fieldObservation({
191
- id: "entity-123.status.current",
192
- field: "registrationStatus",
193
- value: "ACTIVE",
194
- rawSource,
195
- extraction: {
196
- confidence: 0.97,
197
- locator: "json:$.registrationStatus",
198
- extractor: "example-extractor",
199
- extractedAt: new Date().toISOString(),
200
- },
201
- claim: {
202
- subjectType: "public-record.entity",
203
- subjectId: "entity-123",
204
- surface: "example.profile",
205
- claimType: "public-data.field",
206
- status: "proposed",
207
- impactLevel: "medium",
208
- collectedBy: "example-extractor",
209
- },
210
- }))
211
- .build();
212
- ```
213
-
214
- Survey exports `uploadedDocumentSource`, `apiRecordSource`, `webPageSource`,
215
- `manualEntrySource`, and `policyStandardSource`. Producer-provided `id` values
216
- are preserved; otherwise Survey derives a stable id from source kind and
217
- `sourceRef`. Bare checksum values are normalized to `sha256:<value>`, while
218
- already-prefixed checksum values are preserved. Producer metadata is copied
219
- through to Surface evidence.
220
-
221
- Use `policyStandardSource` when the observed material is the applied standard
222
- itself. It records `inlineText`, `standardVersion`, and optional `paragraphRef`
223
- on the `RawSource` and projects to Surface `policy_rule` evidence by default.
224
- Survey only preserves the producer-applied standard text/version; it does not
225
- decide whether that standard is correct for the producer's domain.
226
-
227
- ## Interpretation records
228
-
229
- Use `addInterpretation` when a producer records how an actor read a
230
- `policy-standard` paragraph for one claim. Interpretations are flat provenance
231
- records with an `appliesTo` edge to a claim and an `anchorsTo` edge to a
232
- policy-standard raw source; they are not nested claim derivations or rejection
233
- reasons.
234
-
235
- ```ts
236
- import {
237
- apiRecordSource,
238
- buildSurveyTrustInput,
239
- fieldObservation,
240
- policyStandardSource,
241
- SurveyInputBuilder,
242
- } from "@kontourai/survey";
243
-
244
- const observedAt = new Date().toISOString();
245
- const standard = policyStandardSource({
246
- id: "source.example.policy-standard.rule-1",
247
- sourceRef: "policy-standard://example/rules/2026#rule-1",
248
- observedAt,
249
- inlineText: "A producer reading must cite the applied rule paragraph.",
250
- standardVersion: "2026.1",
251
- paragraphRef: "rule-1",
252
- });
253
-
254
- const surveyInput = new SurveyInputBuilder({ source: "example-producer:run-1" })
255
- .addRawSource(standard)
256
- .addObservation(fieldObservation({
257
- id: "observation.example.policy-application",
258
- field: "policyApplication.status",
259
- value: "DOCUMENTED",
260
- rawSource: apiRecordSource({
261
- id: "source.example.application-record",
262
- sourceRef: "example-records://application/application-1",
263
- observedAt,
264
- checksum: "application-1",
265
- }),
266
- extraction: {
267
- target: "policyApplication.status",
268
- locator: "json:$.policyApplication.status",
269
- extractor: "example-extractor",
270
- extractedAt: observedAt,
271
- },
272
- claim: {
273
- id: "claim.example.policy-application",
274
- subjectType: "example.application",
275
- subjectId: "application-1",
276
- surface: "example.review",
277
- claimType: "policy-application.status",
278
- impactLevel: "medium",
279
- collectedBy: "example-extractor",
280
- },
281
- }))
282
- .addInterpretation({
283
- id: "interpretation.example.rule-1",
284
- appliesToClaimId: "claim.example.policy-application",
285
- anchorsToSourceId: standard.id,
286
- ruleLocator: "text:paragraph=rule-1",
287
- reading: "The producer read rule 1 as applying to the claim.",
288
- actor: "producer-operator",
289
- recordedAt: observedAt,
290
- })
291
- .build();
292
-
293
- const trustInput = buildSurveyTrustInput(surveyInput);
294
- ```
295
-
296
- Projection emits a normal Surface verification event with
297
- `method: "survey-interpretation"`, the existing `claimId`, and anchor
298
- `evidenceIds`. Because current Surface verification events reject unsupported
299
- keys, typed edge details are preserved on the projected claim at
300
- `metadata.survey.interpretations[]`. The anchor evidence uses
301
- `evidenceType: "policy_rule"`, `method: "anchoring"`, the interpretation
302
- `ruleLocator` as `sourceLocator`, and the policy-standard text/version metadata.
303
-
304
- ## Review resources
305
-
306
- Survey also exports producer-neutral `ReviewItem`, `ReviewCandidate`, and
307
- `ReviewDecision` TypeScript resource shapes for review UI and adapter fixtures.
308
- They use `apiVersion`, `kind`, `metadata`, `spec`, and `status` fields while
309
- mapping back to the existing Survey record layer. See
310
- [`docs/review-resource-contract.md`](docs/review-resource-contract.md) for
311
- field ownership, mapping hints, and the `ReviewSession` non-goal.
312
-
313
- ## Field observations
314
-
315
- Use `fieldObservation` when a producer wants to describe one scalar field value
316
- without hand-assembling the repeated source, extraction, candidate, review, and
317
- claim defaults. The helper returns a normal `SurveyObservationInput`, so it
318
- works with `SurveyInputBuilder.addObservation` and the same Surface projection
319
- path.
320
-
321
- ```ts
322
- import {
323
- buildSurveyTrustInput,
324
- fieldObservation,
325
- SurveyInputBuilder,
326
- } from "@kontourai/survey";
327
-
328
- const surveyInput = new SurveyInputBuilder({
329
- source: "example-producer:run-1",
330
- })
331
- .addObservation(fieldObservation({
332
- id: "entity-123.status.current",
333
- field: "registrationStatus",
334
- value: "ACTIVE",
335
- rawSource: {
336
- kind: "api-record",
337
- sourceRef: "example-records://entity/entity-123",
338
- observedAt: new Date().toISOString(),
339
- locatorScheme: "structured-field",
340
- },
341
- extraction: {
342
- confidence: 0.97,
343
- locator: "json:$.registrationStatus",
344
- extractor: "example-extractor",
345
- extractedAt: new Date().toISOString(),
346
- },
347
- reviewOutcome: {
348
- status: "verified",
349
- actor: "records-operator",
350
- reviewedAt: new Date().toISOString(),
351
- },
352
- claim: {
353
- subjectType: "public-record.entity",
354
- subjectId: "entity-123",
355
- surface: "example.profile",
356
- claimType: "public-data.field",
357
- status: "verified",
358
- impactLevel: "medium",
359
- collectedBy: "example-extractor",
360
- },
361
- metadata: {
362
- producerField: "registration_status",
363
- },
364
- }))
365
- .build();
366
-
367
- const trustInput = buildSurveyTrustInput(surveyInput);
368
- ```
369
-
370
- `fieldObservation` sets `extraction.target` and `claim.fieldOrBehavior` from
371
- `field` when omitted, uses the scalar as both the extraction and claim value,
372
- and adds neutral helper metadata at
373
- `metadata.survey.field = { representation: "scalar" }`. Producer metadata is
374
- preserved. Producers still own scalar semantics, validation, candidate ranking,
375
- review policy, and whether a value should be verified, proposed, rejected, or
376
- assumed.
377
-
378
- ## Source-of-authority observations
379
-
380
- Use `sourceOfAuthorityObservationBuilder` when a producer treats the raw source
381
- as authoritative for the extracted target: an official publication,
382
- registration platform page, policy document, contract record, or
383
- system-of-record response. The builder does not decide whether the source is
384
- truly authoritative. It guides producers through the source, extraction,
385
- source-authority posture, review outcome, and claim fields that make the
386
- declared source posture auditable.
387
-
388
- Verified or assumed source-of-authority observations require:
389
-
390
- - source reference
391
- - source locator
392
- - source-authority class
393
- - source-authority scope
394
- - review actor
395
- - reviewed time
396
-
397
- Source-authority metadata projects through Surface Evidence metadata under
398
- `sourceAuthority`. It does not project to Surface `authorityTrace`, which is
399
- reserved for actor, credential, role, organization, policy, or system authority.
400
-
401
- ```ts
402
- import {
403
- buildSurveyTrustInput,
404
- sourceOfAuthorityObservationBuilder,
405
- SurveyInputBuilder,
406
- uploadedDocumentSource,
407
- } from "@kontourai/survey";
408
-
409
- const observedAt = new Date().toISOString();
410
- const rawSource = uploadedDocumentSource({
411
- sourceRef: "https://rules.example.test/thresholds.pdf",
412
- observedAt,
413
- checksum: "abc123",
414
- locatorScheme: "pdf",
415
- });
416
-
417
- const surveyInput = new SurveyInputBuilder({
418
- source: "rule-producer:run-1",
419
- })
420
- .addObservation(sourceOfAuthorityObservationBuilder({
421
- id: "rule.threshold.primary.2026",
422
- field: "regulatedRule.threshold.primary.2026",
423
- value: 1200,
424
- })
425
- .withSourceAuthority({
426
- authorityClass: "official_publication",
427
- scope: {
428
- jurisdiction: "example",
429
- productArea: "regulated-rule",
430
- effectiveYear: 2026,
431
- },
432
- sourceVersion: "2026",
433
- declaredBy: "rule-producer",
434
- })
435
- .fromSource(rawSource)
436
- .withExtraction({
437
- confidence: 0.94,
438
- locator: "pdf:page=12;table=thresholds;row=primary",
439
- extractor: "rule-producer",
440
- extractedAt: observedAt,
441
- })
442
- .withReviewOutcome({
443
- status: "verified",
444
- actor: "rule-reviewer",
445
- reviewedAt: new Date().toISOString(),
446
- })
447
- .forClaim({
448
- subjectType: "regulated-rule",
449
- subjectId: "example:threshold:primary:2026",
450
- surface: "regulated.rules",
451
- claimType: "regulated.rule-value",
452
- status: "verified",
453
- impactLevel: "high",
454
- evidenceType: "policy_rule",
455
- evidenceMethod: "extraction",
456
- collectedBy: "rule-producer",
457
- })
458
- .build())
459
- .build();
460
-
461
- const trustInput = buildSurveyTrustInput(surveyInput);
462
- ```
463
-
464
- `sourceOfAuthorityObservation` remains available as the lower-level object
465
- factory when a producer already has the full observation input assembled.
466
-
467
- Contextual claims such as "this submission is compliant" or "this record is
468
- eligible for a specific requester" are not source-of-authority observations.
469
- They are Surface claims with Claim Dependencies on source-of-authority claims
470
- and other producer facts. The producer owns that domain logic.
471
-
472
- For the reusable producer workflow, including manual confirmation state,
473
- source references, Survey review outcomes, and Surface report boundaries, see
474
- [Source-Authority Review Pattern](docs/source-authority-review-pattern.md).
475
-
476
- ## Reviewed candidate resolutions
477
-
478
- Use `reviewedCandidateResolution` when a producer has multiple candidate
479
- observations for the same target and a review outcome selects one candidate.
480
- The helper wraps `candidateReviewRecord`, attaches the review outcome to the
481
- selected candidate, defaults the candidate set to `resolved`, defaults the
482
- selected claim status from the review outcome, and defaults unselected
483
- candidates to `superseded`. Producers can override selected or unselected claim
484
- statuses when their domain workflow needs a different posture.
485
-
486
- This is useful for corrected documents, source-of-truth choices, and review
487
- queues where losing candidates should remain visible for transparency rather
488
- than disappearing from the trust trail.
489
-
490
- Candidates may include an optional `rejectionReason` when a producer wants to
491
- record why a non-selected alternative was superseded or rejected. Survey
492
- preserves that producer-provided rationale on the candidate and projects it to
493
- Surface claim `metadata.survey.candidate.rejectionReason` for that candidate
494
- while preserving producer-provided `metadata.survey` keys. Survey does not rank
495
- candidates, choose winners, or define rejection policy.
496
-
497
- ## Reviewed current/proposed resolutions
498
-
499
- Use `reviewedCurrentProposedResolution` when a producer has exactly two
500
- candidate roles for the same target: the current value the producer would keep
501
- absent a change, and a proposed value introduced by new source material,
502
- extraction, or review work. The helper consumes full observations, selects
503
- either the current or proposed candidate through `selectedCandidateRole`, and
504
- wraps the result with `reviewedCandidateResolution`.
505
-
506
- The helper may promote the selected candidate to a caller-supplied
507
- `selectedClaimId`. The unselected observation keeps its caller-authored claim
508
- id, so producers can keep losing candidates as candidate-specific history.
509
- Survey does not decide producer policy: callers still own review status,
510
- selected and unselected claim statuses, source details, claim vocabulary, and
511
- domain metadata.
512
-
513
- ## Repeated observations
514
-
515
- Use `repeatedObservation` when a producer wants to describe a repeated field or
516
- entity list as one aggregate observation. The helper returns a normal
517
- `SurveyObservationInput`, so it works with `SurveyInputBuilder.addObservation`
518
- and the same Surface projection path.
519
-
520
- ```ts
521
- import {
522
- buildSurveyTrustInput,
523
- repeatedObservation,
524
- SurveyInputBuilder,
525
- } from "@kontourai/survey";
526
-
527
- const aliases = [
528
- { name: "North Annex", sourceLabel: "record row 1" },
529
- { name: "East Annex", sourceLabel: "record row 2" },
530
- ];
531
-
532
- const surveyInput = new SurveyInputBuilder({
533
- source: "example-producer:run-1",
534
- })
535
- .addObservation(repeatedObservation({
536
- id: "entity-123.aliases.current",
537
- field: "knownAliases",
538
- value: aliases,
539
- rawSource: {
540
- kind: "api-record",
541
- sourceRef: "example-records://entity/entity-123",
542
- observedAt: new Date().toISOString(),
543
- locatorScheme: "structured-field",
544
- },
545
- extraction: {
546
- confidence: 0.88,
547
- locator: "json:$.aliases",
548
- extractor: "example-extractor",
549
- extractedAt: new Date().toISOString(),
550
- },
551
- reviewOutcome: {
552
- status: "verified",
553
- actor: "records-operator",
554
- reviewedAt: new Date().toISOString(),
555
- },
556
- claim: {
557
- subjectType: "public-record.entity",
558
- subjectId: "entity-123",
559
- surface: "example.profile",
560
- claimType: "public-data.repeated-field",
561
- status: "verified",
562
- impactLevel: "medium",
563
- collectedBy: "example-extractor",
564
- },
565
- metadata: {
566
- producerField: "aliases",
567
- },
568
- }))
569
- .build();
570
-
571
- const trustInput = buildSurveyTrustInput(surveyInput);
572
- ```
126
+ `@kontourai/survey/review-workbench/standalone.css` exists for pages Survey owns entirely. Server code persisting browser-submitted review events should use `persistReviewSessionEvents`, then `deriveReviewSessionApplyResultForSnapshot` (or the composed `deriveServerReviewSessionApplyResult` with the freshness and event assertions from `@kontourai/survey/review-workbench/server-review-session`) before applying product policy — write results derive from pre-decision snapshots plus persisted events, never from browser-computed decisions.
573
127
 
574
- `repeatedObservation` sets `extraction.target` and
575
- `claim.fieldOrBehavior` from `field` when omitted, uses the array as both the
576
- extraction and claim value, and adds neutral helper metadata at
577
- `metadata.survey.repeated = { representation: "aggregate-array", itemCount }`.
578
- Producer metadata is preserved. Producers still own item semantics,
579
- validation, candidate ranking, review policy, and whether a value should be
580
- verified, proposed, rejected, or assumed.
128
+ The [Consumer Integration Guide](docs/consumer-integration-guide.md) covers the full path from `ReviewItem` construction through persisted review events, exported results, and optional Surface projection, with test-covered examples under [`examples/review-workbench/`](examples/review-workbench/).
581
129
 
582
- ## Candidate review records
130
+ ## Where Survey fits
583
131
 
584
- Use `candidateReviewRecord` when a producer has multiple candidate observations
585
- for the same target and wants Survey to assemble the shared candidate set,
586
- candidate links, and optional review outcome.
132
+ Kontour AI shows the work behind AI. Survey is the producer-side primitive:
587
133
 
588
- ```ts
589
- import {
590
- candidateReviewRecord,
591
- fieldObservation,
592
- SurveyInputBuilder,
593
- } from "@kontourai/survey";
594
-
595
- const observations = [
596
- fieldObservation({
597
- id: "entity-123.status.registry",
598
- field: "registrationStatus",
599
- value: "ACTIVE",
600
- rawSource: {
601
- kind: "api-record",
602
- sourceRef: "example-records://entity/entity-123",
603
- observedAt: new Date().toISOString(),
604
- locatorScheme: "structured-field",
605
- },
606
- extraction: {
607
- confidence: 0.97,
608
- locator: "json:$.registrationStatus",
609
- extractor: "example-extractor",
610
- extractedAt: new Date().toISOString(),
611
- },
612
- candidate: { id: "candidate.registry", confidence: 0.97 },
613
- claim: {
614
- id: "claim.entity-123.status.registry",
615
- subjectType: "public-record.entity",
616
- subjectId: "entity-123",
617
- surface: "example.profile",
618
- claimType: "public-data.field",
619
- status: "verified",
620
- impactLevel: "medium",
621
- collectedBy: "example-extractor",
622
- },
623
- }),
624
- fieldObservation({
625
- id: "entity-123.status.archive",
626
- field: "registrationStatus",
627
- value: "INACTIVE",
628
- rawSource: {
629
- kind: "web-page",
630
- sourceRef: "https://records.example.test/entity-123",
631
- observedAt: new Date().toISOString(),
632
- locatorScheme: "html",
633
- },
634
- extraction: {
635
- confidence: 0.71,
636
- locator: "css:#registration-status",
637
- extractor: "example-crawler",
638
- extractedAt: new Date().toISOString(),
639
- },
640
- candidate: { id: "candidate.archive", confidence: 0.71 },
641
- claim: {
642
- id: "claim.entity-123.status.archive",
643
- subjectType: "public-record.entity",
644
- subjectId: "entity-123",
645
- surface: "example.profile",
646
- claimType: "public-data.field",
647
- status: "superseded",
648
- impactLevel: "medium",
649
- collectedBy: "example-crawler",
650
- },
651
- }),
652
- ];
653
-
654
- const surveyInput = new SurveyInputBuilder({ source: "example-producer:run-1" })
655
- .addClaimRecords(candidateReviewRecord({
656
- id: "candidate-set.entity-123.registration-status",
657
- target: "registrationStatus",
658
- selectedCandidateId: "candidate.registry",
659
- status: "resolved",
660
- rationale: "Registry source wins over archive source.",
661
- reviewOutcome: {
662
- status: "verified",
663
- actor: "records-operator",
664
- reviewedAt: new Date().toISOString(),
665
- },
666
- observations,
667
- }))
668
- .build();
669
- ```
670
-
671
- `candidateReviewRecord` does not choose the winning candidate or status. The
672
- producer still supplies candidate ids, selected candidate id, claim ids, review
673
- status, rationale, and all domain policy. Survey only assembles the generic
674
- record graph and tolerates repeated references to identical raw sources or the
675
- shared candidate set while rejecting conflicting duplicate ids. Duplicate
676
- conflict checks assume Survey records are JSON-shaped data, which is the same
677
- shape expected by Surface validation and reports.
678
-
679
- If an observation candidate includes `rejectionReason`, `candidateReviewRecord`
680
- preserves it in the shared candidate set. Use this only for producer-authored
681
- rationale about a candidate that the producer already treats as non-selected,
682
- superseded, or rejected; it does not affect selected candidate behavior or
683
- status projection.
684
-
685
- A candidate set with status `"conflict"` represents a Survey-side Candidate
686
- Conflict before review has resolved which candidate should win. When no review
687
- outcome overrides it, `buildSurveyTrustInput` projects the claim to Surface
688
- status `"disputed"` and records a `"candidate-conflict"` verification event.
689
-
690
- ## Review proofs
691
-
692
- Use review proof helpers when a producer wants a Surface-compatible integrity
693
- anchor for one reviewed Survey source -> extraction -> candidate -> review ->
694
- claim path.
695
-
696
- ```ts
697
- import {
698
- buildCanonicalReviewProofPayload,
699
- buildReviewProofAnchor,
700
- canonicalReviewProofJson,
701
- hashCanonicalReviewProofPayload,
702
- } from "@kontourai/survey";
703
-
704
- const proofInput = {
705
- rawSource,
706
- extraction,
707
- candidate,
708
- candidateSet,
709
- reviewOutcome,
710
- claim,
711
- };
712
-
713
- const payload = buildCanonicalReviewProofPayload(proofInput);
714
- const canonicalJson = canonicalReviewProofJson(payload);
715
- const hash = hashCanonicalReviewProofPayload(payload);
716
- const anchor = buildReviewProofAnchor(proofInput);
717
- ```
718
-
719
- `buildReviewProofAnchor` returns a hash-only Surface `IntegrityAnchor` for the
720
- canonical payload. The lower-level payload, JSON, and hash helpers are exported
721
- so producers can store or recompute the exact canonical proof material used for
722
- the anchor. Producer metadata is not part of the canonical payload; any
723
- non-portable context belongs outside the hash, such as anchor metadata.
724
-
725
- The canonical payload is the portable review proof contract. It contains:
726
-
727
- | Field | Purpose |
134
+ | Product | Owns |
728
135
  | --- | --- |
729
- | `schemaVersion` / `proof.schema` / `proof.schemaVersion` | Stable Survey review proof schema identity. |
730
- | `proof.packageName` / `proof.packageVersion` | Review proof contract identity. `proof.packageVersion` is the proof contract version, not the npm package release version. Package releases do not change canonical proof hashes unless this explicit proof contract version or another canonical field changes. |
731
- | `proof.issuer` | Survey producer identity, derived from the claim collector. |
732
- | `proof.producer` | Extraction producer identity, derived from the extractor id. |
733
- | `proof.issuedAt` | Proof envelope time, derived from review time, then claim update time, then extraction time. |
734
- | `proof.subject` | Claim identity: claim id, candidate set id, reviewed candidate id, subject, surface, claim type, and field/behavior. If the claim also names a candidate id, it must match the reviewed candidate id. |
735
- | `proof.sourcePayload` / `rawSource.checksum` | Source payload identity, ref, and producer-supplied checksum when present. |
736
- | `extraction` | Extracted target, value, locator, excerpt, extractor, confidence, and extraction time. |
737
- | `candidate` / `candidateSet` | Candidate identity/value plus the ordered candidate set, selected candidate, status, and rationale. |
738
- | `reviewOutcome` | Review decision/status, actor, review time, rationale, and evidence ids. |
739
- | `claim` | Projected claim identity, status/value, impact, evidence method, derivation links, collector, actor, and event method. |
740
-
741
- To recompute the anchor value, rebuild the same canonical payload from the
742
- reviewed Survey records, call `canonicalReviewProofJson(payload)`, and compute
743
- SHA-256 over that JSON. The result should equal
744
- `claim.currentIntegrityAnchor.value`. The Surface anchor remains generic:
745
- `kind: "hash"`, `algorithm: "sha256"`, `verificationStatus: "unverified"`, no
746
- Survey-specific anchor metadata, and a source/time pointer for display. Claims
747
- without a selected review outcome are not anchored by `{ reviewProofs: true }`.
748
-
749
- When the Surface projection proof option is enabled, `buildSurveyTrustInput`
750
- will attach the same kind of anchor to the projected reviewed claim:
751
-
752
- ```ts
753
- const trustInput = buildSurveyTrustInput(surveyInput, { reviewProofs: true });
754
- ```
755
-
756
- The proof provides hash-only tamper evidence for the Survey review/provenance
757
- trail in the canonical payload. It does not authenticate an actor, sign the
758
- payload, or prove the real-world truth of the claim. Non-goals include JWT/JWS
759
- signing, key management, a transparency log, and any veracity guarantee.
760
-
761
- JWT-adjacent words in the payload are process-envelope vocabulary, not a v0 JWT
762
- implementation. `issuer` identifies the Survey producer for recomputation,
763
- `subject` identifies the claim being reviewed, and `issuedAt` records the review
764
- proof time. Audience restrictions, expiry, cryptographic signing, key discovery,
765
- and legal non-repudiation are deferred concepts for a future signed envelope.
766
-
767
- ## Computed values
768
-
769
- Computed values are normal `ClaimTarget` entries in `claims`. Producers should
770
- link them to their inputs with Surface Claim Dependency fields:
771
- `derivedFrom` for simple claim-id links, or `derivationEdges` when the link
772
- needs method, role, support-strength, rationale, or metadata.
773
-
774
- Survey passes those fields through to Surface while keeping the same
775
- source -> extraction -> candidate -> review -> claim projection path. Surface
776
- owns dependency semantics such as recompute pressure and status ceilings.
777
-
778
- ## Adversarial passes
779
-
780
- Producers that run a second adversarial pass — whether an LLM judge, a rules
781
- engine, or a second human reviewer — emit their output into Survey as a normal
782
- producer pass with a distinct `extractor` id. Survey does not know or care that
783
- a second pass ran; it sees two producers disagreeing on the same target, which
784
- is exactly what `conflict` and escalation records are for.
136
+ | **Survey** | Producer evidence: source → extraction → candidate → review → claim |
137
+ | **[Surface](https://kontourai.io/surface)** | Portable trust state: claims, evidence, policies, trust snapshots |
138
+ | **[Flow](https://kontourai.io/flow)** | Process transparency: steps, gates, transitions, runs, exceptions |
139
+ | **[Veritas](https://kontourai.io/veritas)** | Code/change transparency: repo standards, merge readiness |
140
+ | **[Flow Agents](https://kontourai.io/flow-agents)** | Agent-facing distribution: skills, kits, runtime adapters, hooks |
785
141
 
786
- Two patterns cover the adversary's output:
142
+ Survey feeds Surface; Surface-shaped evidence feeds Flow gates; Flow's adversarial route-back pattern consumes Survey's per-round adversarial-pass records. Each product stands alone — Survey only requires `@kontourai/surface`.
787
143
 
788
- **Conflicting candidate.** The adversary disagrees with the first-pass extraction
789
- value. Add the adversary's extraction as a second candidate to the same candidate
790
- set using `candidateReviewRecord` with `status: "conflict"`. Survey projects the
791
- conflict to a `disputed` claim in Surface.
144
+ ## Documentation
792
145
 
793
- ```ts
794
- import { candidateReviewRecord, fieldObservation, SurveyInputBuilder } from "@kontourai/survey";
795
-
796
- const records = candidateReviewRecord({
797
- id: "candidate-set.entity-1.registration-status",
798
- target: "registrationStatus",
799
- status: "conflict",
800
- rationale: "First pass and adversary disagree; human review required.",
801
- observations: [
802
- fieldObservation({
803
- id: "observation.entity-1.status.first-pass",
804
- field: "registrationStatus",
805
- value: "ACTIVE",
806
- rawSource: {
807
- kind: "api-record",
808
- sourceRef: "records://entity-1/registry",
809
- observedAt: new Date().toISOString(),
810
- locatorScheme: "structured-field",
811
- },
812
- extraction: {
813
- confidence: 0.91,
814
- locator: "json:$.registrationStatus",
815
- extractor: "agent-v1",
816
- extractedAt: new Date().toISOString(),
817
- },
818
- candidate: { id: "candidate.first-pass", confidence: 0.91 },
819
- claim: {
820
- subjectType: "public-record.entity",
821
- subjectId: "entity-1",
822
- surface: "public-record.profile",
823
- claimType: "public-data.field",
824
- impactLevel: "high",
825
- collectedBy: "agent-v1",
826
- },
827
- }),
828
- fieldObservation({
829
- id: "observation.entity-1.status.adversary",
830
- field: "registrationStatus",
831
- value: "INACTIVE",
832
- rawSource: {
833
- kind: "api-record",
834
- sourceRef: "records://entity-1/registry",
835
- observedAt: new Date().toISOString(),
836
- locatorScheme: "structured-field",
837
- },
838
- extraction: {
839
- confidence: 0.84,
840
- locator: "json:$.registrationStatus",
841
- extractor: "adversary-v1",
842
- extractedAt: new Date().toISOString(),
843
- },
844
- candidate: { id: "candidate.adversary", confidence: 0.84 },
845
- claim: {
846
- subjectType: "public-record.entity",
847
- subjectId: "entity-1",
848
- surface: "public-record.profile",
849
- claimType: "public-data.field",
850
- impactLevel: "high",
851
- collectedBy: "adversary-v1",
852
- },
853
- }),
854
- ],
855
- });
856
- ```
857
-
858
- **Framing challenge.** The adversary identifies a target that was not addressed
859
- at all — a missed standard, an unconsidered alternative, or a misframed question.
860
- Use `addEscalation` to record the challenge. Attach it to the closest relevant
861
- claim with `attachToClaimId`; Survey projects it as an additional `disputed`
862
- verification event on that claim so the reviewer sees it prominently.
863
-
864
- ```ts
865
- import { SurveyInputBuilder, fieldObservation } from "@kontourai/survey";
866
-
867
- const builder = new SurveyInputBuilder({ source: "example-producer:run-2" });
868
-
869
- // First-pass observation
870
- builder.addObservation(fieldObservation({ /* ... */ }));
871
-
872
- // Adversary raises a framing challenge
873
- builder.addEscalation({
874
- id: "escalation.entity-1.fair-value.completeness",
875
- target: "fairValue",
876
- dimension: "completeness",
877
- reason: "Measurement standard Level 3 inputs were not documented; sensitivity range and unobservable input assumptions are missing.",
878
- raisedBy: "adversary-v1",
879
- raisedAt: new Date().toISOString(),
880
- attachToClaimId: "claim.entity-1.fair-value",
881
- });
882
- ```
883
-
884
- If a subsequent first-pass or human-review pass resolves the challenge, set
885
- `resolvedBy` to the id of the observation that closes it. Survey will not project
886
- a `disputed` event for resolved escalations.
146
+ | Guide | What it covers |
147
+ | --- | --- |
148
+ | [Record Contracts](docs/record-contracts.md) | every record shape in the chain: raw sources, extractions, candidates, reviews, proofs, resolutions, comfort-zone flags |
149
+ | [Adversarial Passes & Learning](docs/adversarial-and-learning.md) | per-round adversarial review records and learning projections, and the Flow boundary |
150
+ | [Consumer Integration Guide](docs/consumer-integration-guide.md) | the full consumer path: ReviewItem queues, the workbench, persisted events, the server apply boundary |
151
+ | [Review Resource Contract](docs/review-resource-contract.md) | the Kontour Resource shapes for review sessions and events |
152
+ | [Source-Authority Review Pattern](docs/source-authority-review-pattern.md) | record discipline for sources the producer treats as authoritative |
153
+ | [Review Workbench Prototype](docs/review-workbench-prototype.md) | running the fixture-backed standalone demo locally |
154
+ | [Releasing](docs/RELEASING.md) | release prep and publish flow |
887
155
 
888
- Escalation dimensions follow the adversary's attack surface: `framing` (wrong
889
- question framed), `completeness` (missing standards, alternatives, or evidence),
890
- `conclusion` (reasoning would not survive challenge), and `citation` (cited
891
- sources do not support the claims attached to them).
156
+ ## Product boundary
892
157
 
893
- Framing challenges without an `attachToClaimId` are carried in `SurveyInput`
894
- for producer tooling but are not projected to Surface. If the adversary cannot
895
- identify a target claim to attach a framing challenge to, emit a candidate set
896
- with `status: "escalated"` for the affected target — that projects to `disputed`
897
- in Surface with a `candidate-escalation` event.
158
+ Survey does not crawl pages, parse PDFs, rank candidates, decide review policy, or claim a value is true. Producers own acquisition, extraction, ranking, review UX, materiality, and domain policy. Survey gives those producers a consistent source → extraction → candidate → review → claim contract before the records enter Surface.
898
159
 
899
- ## Comfort zone flags
160
+ ## Contributor checks
900
161
 
901
- Use `withinComfortZone: false` on a `ReviewOutcome` when the reviewer is
902
- recording a decision outside their domain expertise or is flagging that the
903
- conclusion requires a different authority to confirm. The flag and optional
904
- `comfortZoneNote` are carried forward as structured Survey metadata on the
905
- projected Surface claim at `metadata.survey.comfortZone`. Verification event
906
- `notes` carry the normal review or candidate-set rationale only.
162
+ Install the repo-owned Git hooks once per clone:
907
163
 
908
- ```ts
909
- reviewOutcome: {
910
- status: "assumed",
911
- actor: "records-operator",
912
- reviewedAt: new Date().toISOString(),
913
- rationale: "Assumed from registry source pending specialist review.",
914
- withinComfortZone: false,
915
- comfortZoneNote: "Renewal clause interpretation requires specialist counsel.",
916
- },
164
+ ```bash
165
+ npm run setup:repo-hooks
917
166
  ```
918
167
 
919
- ## Learning projections
920
-
921
- Use `buildSurveyLearningProjections(input)` when producer or review tooling needs
922
- workflow/evaluation signals without changing Surface `TrustInput`.
168
+ The setup command is idempotent. It sets this repo's local `core.hooksPath` to `.githooks` and does not require global Git configuration. Validate hook drift or package health directly:
923
169
 
924
- ```ts
925
- import {
926
- buildSurveyLearningProjections,
927
- buildSurveyTrustInput,
928
- } from "@kontourai/survey";
929
-
930
- const learning = buildSurveyLearningProjections(surveyInput);
931
- const trustInput = buildSurveyTrustInput(surveyInput);
170
+ ```bash
171
+ npm run validate:repo-hooks
172
+ npm run verify
932
173
  ```
933
174
 
934
- Learning projections are product-neutral `learning.*` records. Survey emits
935
- `learning.rejected-candidate` from structured candidate rejection data such as
936
- non-empty `Candidate.rejectionReason` values or a candidate-specific
937
- `ReviewOutcome.status === "rejected"` outcome with rationale. When both exist,
938
- Survey emits one rejected-candidate projection enriched with candidate and
939
- review outcome context.
940
- Ordinary rejected candidates do not emit `learning.comfort-zone`.
941
-
942
- Survey also emits `learning.comfort-zone` from structured
943
- `ReviewOutcome.withinComfortZone === false` data and `learning.escalation` from
944
- unresolved `EscalationRecord`s, including unattached records that producer
945
- tooling can route but Surface cannot attach to a claim event.
946
-
947
- These projections are producer/review workflow and evaluation signals. They are
948
- not claims about truth or veracity, not Surface claim status, not evidence, and
949
- not verification events. Calling `buildSurveyLearningProjections` does not alter
950
- `buildSurveyTrustInput`, trust status derivation, or escalation event projection.
951
-
952
- ## Product Boundary
953
-
954
- Survey does not crawl pages, parse PDFs, rank candidates, decide review policy,
955
- or claim a value is true. Producers own acquisition, extraction, ranking, review
956
- UX, materiality, and domain policy. Survey gives those producers a consistent
957
- source -> extraction -> candidate -> review -> claim contract before the records
958
- enter Surface.
175
+ The committed pre-push hook runs both commands from the repo root.
959
176
 
960
- ## Commands
177
+ ## License
961
178
 
962
- ```sh
963
- npm install
964
- npm run verify
965
- ```
179
+ [Apache-2.0](LICENSE) © Kontour AI
@@ -7,6 +7,8 @@ export { reviewedCandidateResolution } from "./reviewed-candidate-resolution.js"
7
7
  export type { ReviewedCandidateResolutionInput } from "./reviewed-candidate-resolution.js";
8
8
  export { reviewedCurrentProposedResolution } from "./reviewed-current-proposed-resolution.js";
9
9
  export type { CurrentProposedCandidateRole, ReviewedCurrentProposedResolutionInput, } from "./reviewed-current-proposed-resolution.js";
10
+ export { flowTrustArtifactFromReviewOutcome } from "./to-flow-artifact.js";
11
+ export type { FlowTrustArtifact, FlowTrustArtifactOptions } from "./to-flow-artifact.js";
10
12
  export { buildSurveyTrustInput } from "./to-surface.js";
11
13
  export type { BuildSurveyTrustInputOptions } from "./to-surface.js";
12
14
  export { buildSurveyLearningProjections } from "./learning-projections.js";
package/dist/src/index.js CHANGED
@@ -2,6 +2,7 @@ export { reviewResourceApiVersion } from "./review-resource.js";
2
2
  export { candidateReviewRecord, SurveyInputBuilder } from "./builder.js";
3
3
  export { reviewedCandidateResolution } from "./reviewed-candidate-resolution.js";
4
4
  export { reviewedCurrentProposedResolution } from "./reviewed-current-proposed-resolution.js";
5
+ export { flowTrustArtifactFromReviewOutcome } from "./to-flow-artifact.js";
5
6
  export { buildSurveyTrustInput } from "./to-surface.js";
6
7
  export { buildSurveyLearningProjections } from "./learning-projections.js";
7
8
  export { buildCanonicalReviewProofPayload, buildReviewProofAnchor, canonicalReviewProofJson, hashCanonicalReviewProofPayload, REVIEW_PROOF_CONTRACT_VERSION, REVIEW_PROOF_PACKAGE_NAME, REVIEW_PROOF_SCHEMA, REVIEW_PROOF_SCHEMA_VERSION, } from "./review-proof.js";
@@ -0,0 +1,41 @@
1
+ import type { ReviewOutcome, ReviewStatus } from "./types.js";
2
+ /**
3
+ * The neutral trust-artifact shape Kontour Flow consumes through
4
+ * `flow attach-evidence --trust-artifact`. Flow evaluates only these fields
5
+ * plus its own definition and project config; Survey stays the producer-side
6
+ * authority for what the review actually decided.
7
+ */
8
+ export interface FlowTrustArtifact {
9
+ schema_version: "0.1";
10
+ artifact_type: "trust-report";
11
+ subject: string;
12
+ producer: string;
13
+ status: string;
14
+ issued_at: string;
15
+ authority_traces: string[];
16
+ claims: Array<{
17
+ type: string;
18
+ subject: string;
19
+ status: string;
20
+ }>;
21
+ }
22
+ export interface FlowTrustArtifactOptions {
23
+ /** Flow claim type the gate expects, e.g. "adversarial.review". */
24
+ claimType: string;
25
+ /** Flow claim subject the gate expects, e.g. "adversarial-pass.review". */
26
+ subject: string;
27
+ /** Producer identity recorded on the artifact, e.g. "survey/adversarial-workbench". */
28
+ producer: string;
29
+ /** Defaults to the review outcome's reviewedAt, then the current time. */
30
+ issuedAt?: string;
31
+ /** Defaults to ["survey:review-outcome/<outcome id>"]. */
32
+ authorityTraces?: string[];
33
+ /** Override the ReviewStatus -> artifact status projection per entry. */
34
+ statusMap?: Partial<Record<ReviewStatus, string>>;
35
+ }
36
+ /**
37
+ * Projects a Survey ReviewOutcome into the neutral trust artifact Flow
38
+ * consumes, so a per-round review (including an adversarial pass) can satisfy
39
+ * or fail a Flow gate without Flow learning Survey vocabulary.
40
+ */
41
+ export declare function flowTrustArtifactFromReviewOutcome(outcome: ReviewOutcome, options: FlowTrustArtifactOptions): FlowTrustArtifact;
@@ -0,0 +1,31 @@
1
+ const defaultStatusMap = {
2
+ verified: "trusted",
3
+ assumed: "assumed",
4
+ proposed: "proposed",
5
+ rejected: "rejected",
6
+ };
7
+ /**
8
+ * Projects a Survey ReviewOutcome into the neutral trust artifact Flow
9
+ * consumes, so a per-round review (including an adversarial pass) can satisfy
10
+ * or fail a Flow gate without Flow learning Survey vocabulary.
11
+ */
12
+ export function flowTrustArtifactFromReviewOutcome(outcome, options) {
13
+ if (!outcome.id) {
14
+ throw new Error("flowTrustArtifactFromReviewOutcome requires a review outcome id");
15
+ }
16
+ const statusMap = { ...defaultStatusMap, ...options.statusMap };
17
+ const status = statusMap[outcome.status];
18
+ if (!status) {
19
+ throw new Error(`flowTrustArtifactFromReviewOutcome: unmapped review status '${outcome.status}'`);
20
+ }
21
+ return {
22
+ schema_version: "0.1",
23
+ artifact_type: "trust-report",
24
+ subject: options.subject,
25
+ producer: options.producer,
26
+ status,
27
+ issued_at: options.issuedAt ?? outcome.reviewedAt ?? new Date().toISOString(),
28
+ authority_traces: options.authorityTraces ?? [`survey:review-outcome/${outcome.id}`],
29
+ claims: [{ type: options.claimType, subject: options.subject, status }],
30
+ };
31
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontourai/survey",
3
- "version": "0.4.23",
3
+ "version": "0.4.24",
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",
@@ -9,7 +9,7 @@
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/kontourai/survey.git"
11
11
  },
12
- "homepage": "https://kontourai.io/survey",
12
+ "homepage": "https://kontourai.github.io/survey/",
13
13
  "bugs": {
14
14
  "url": "https://github.com/kontourai/survey/issues"
15
15
  },
@@ -52,7 +52,9 @@
52
52
  "check:review-workbench": "npm run check:review-workbench:static",
53
53
  "check:review-workbench:static": "npm run build && node scripts/check-review-workbench.cjs",
54
54
  "setup:repo-hooks": "node scripts/setup-repo-hooks.cjs",
55
- "validate:repo-hooks": "node scripts/validate-repo-hooks.cjs"
55
+ "validate:repo-hooks": "node scripts/validate-repo-hooks.cjs",
56
+ "docs:build": "npm run build --silent && node scripts/docs-site/build.ts",
57
+ "docs:check": "npm run docs:build && node scripts/docs-site/check-links.ts"
56
58
  },
57
59
  "dependencies": {
58
60
  "@kontourai/surface": "^0.5.1"
@@ -61,6 +63,7 @@
61
63
  "@kontourai/console-kit": "^0.1.0",
62
64
  "@playwright/test": "^1.60.0",
63
65
  "@types/node": "^25.6.0",
66
+ "marked": "^16.4.2",
64
67
  "typescript": "^5.8.0"
65
68
  },
66
69
  "engines": {