Kontour Survey#

The producer side of trust. Survey carries evidence from raw source to reviewed claim — without ever pretending to know what's true.

npm version CI Docs License: Apache-2.0

Documentation · Record Contracts · Consumer Guide · kontourai.io/survey


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.

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 Trust Bundles — so downstream trust reports, consoles, and Flow gates can see not just the value, but the evidence and the review posture behind it.

Survey does not decide whether a real-world value is true. It preserves the producer's evidence and review discipline so something downstream can.

What you get#

  • 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.
  • One projection to Surface. buildSurveyTrustBundle turns Survey records into a Surface Trust Bundle; Surface owns claims, evidence, status, and trust reporting from there.
  • 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.
  • 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.
  • Adversarial-pass records for high-stakes review loops, designed to serve as per-round evidence for Flow's adversarial route-back pattern.

See it#

The Review Workbench rendering a real example queue — current vs proposed values, source evidence, decision effect, and the Surface projection preview. Open the live demo.

Survey Review Workbench showing a review queue, current versus proposed values, decision controls, and a Surface preview

Quickstart#

npm install @kontourai/survey @kontourai/surface@^4

Survey supports @kontourai/surface 2.13 and later 2.x, 3.x, and 4.x. CI tests 2.13.0 and the newest 3.x and 4.x. Install a Surface version in that range so your project and Survey share one Surface copy (npm ls @kontourai/surface shows one entry). Without a version, npm installs the latest Surface, which can fall outside that range; npm then installs a separate copy for Survey.

Requires Node.js >=22. TypeScript >=5.0 is required to compile against Survey's published type declarations (defineProductVocabulary's const type parameters are TS 5.0+ syntax) — JavaScript consumers are unaffected; TypeScript consumers on an older compiler should stay on 1.2.x. See the Upgrade Guide for details.

import { buildTrustReport, validateTrustBundle } from "@kontourai/surface";
import { buildSurveyTrustBundle, SurveyInputBuilder } from "@kontourai/survey";

const surveyInput = new SurveyInputBuilder({
  source: "example-producer:run-1",
})
  .addObservation({
    id: "listing-123.availability.current",
    rawSource: {
      kind: "web-page",
      sourceRef: "https://example.test/listings/123",
      observedAt: new Date().toISOString(),
      locatorScheme: "html",
    },
    extraction: {
      target: "availabilityStatus",
      value: "AVAILABLE",
      confidence: 0.92,
      locator: "html:field=availabilityStatus",
      excerpt: "Availability is open.",
      extractor: "example-crawler",
      extractedAt: new Date().toISOString(),
    },
    reviewOutcome: {
      status: "verified",
      actor: "example-operator",
      reviewedAt: new Date().toISOString(),
    },
    claim: {
      subjectType: "public-record.entity",
      subjectId: "listing-123",
      facet: "public-record.profile",
      claimType: "public-data.field",
      fieldOrBehavior: "availabilityStatus",
      impactLevel: "medium",
      collectedBy: "example-crawler",
    },
  })
  .build();

const trustBundle = validateTrustBundle(buildSurveyTrustBundle(surveyInput));
const report = buildTrustReport(trustBundle);

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.

The producer validation path#

  1. Build Survey observations covering each link in the evidence chain.
  2. Call buildSurveyTrustBundle to project the Survey records into a Surface Trust Bundle.
  3. Call Surface validateTrustBundle on the Trust Bundle.
  4. Optionally call public Surface report APIs such as buildTrustReport to inspect claims, evidence, status, gaps, and metadata.

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 evidence chain records needed by Surface.

Model-backed producers implement MappingProposer or UtteranceClaimExtractor in the product that owns the prompt, parsing, runtime policy, and domain mapping. Survey accepts their normalized proposals through the same framework-neutral interfaces and review path; it does not acquire credentials or depend on an AI runtime.

When you build an authorized-action authorizing block outside the workbench, pair buildAuthorizedActionAuthorizing with buildPromptRef({ module, component, version?, scheme? }) — buildPromptRef formats a well-formed, versioned promptRef (bare "review-workbench/decision-card@v1" or scheme-prefixed "survey://<module>/<component>@v1") that buildAuthorizedActionAuthorizing accepts directly, instead of hand-formatting the string.

Review lifecycle#

ReviewOutcome.resolution optionally records the terminal result of a review round while preserving compatibility with outcomes that infer their result from status. The explicit could_not_confirm resolution requires a non-empty resolutionReason; attemptEvidenceIds may record evidence of what the reviewer tried. It is valid only with an unchanged proposed or assumed status. Other explicit resolutions must also agree with status: accepted is verified/assumed, rejected is rejected, and held is any non-rejected retained posture.

Could-not-confirm is terminal for the review round, but it is not rejection, verification, or escalation. Surface projection stays quiet: the claim keeps its pre-review status, no attempt evidence is projected, and no review-derived freshness, validity, gap, or verification posture is added. The outcome remains available in Survey review records, canonical review proof v3, and the distinct learning.could-not-confirm producer-learning signal.

Review Workbench embed#

Web component (shadow DOM, no framework required):

<link rel="stylesheet" href="@kontourai/survey/review-workbench/standalone.css">
<survey-review-workbench theme="survey" color-scheme="dark"></survey-review-workbench>
<script type="module">
  import "@kontourai/survey/review-workbench/element";
  const el = document.querySelector("survey-review-workbench");
  el.session = reviewQueueSession;
  el.presentationAdapter = myAdapter;
</script>

Direct mount into an existing element:

import {
  mountReviewWorkbench,
  type ReviewPresentationAdapter,
} from "@kontourai/survey/review-workbench";
import "@kontourai/survey/review-workbench.css";

const presentationAdapter = {
  labelForTarget: (target) => target === "registrationStatus"
    ? "Registration status"
    : undefined,
  linkForReviewItem: (item) => ({ href: `/review/${item.metadata.name}` }),
} satisfies ReviewPresentationAdapter;

mountReviewWorkbench(element, reviewQueueSession, { presentationAdapter });

The embedded stylesheet is scoped to .survey-workbench-embed and bundles Console Kit tokens, so it will not rewrite the host application's body or :root styles. Mount into:

<div class="survey-workbench-embed theme-survey"></div>

@kontourai/survey/review-workbench/standalone.css exists for pages Survey owns entirely.

Theme it as your own brand#

The bundled stylesheet carries a literal default for every --k-* token on the embed element itself, so the workbench looks right in a host that declares no tokens at all. To re-brand it, declare the tokens you want on the embed element — a rule matching .survey-workbench-embed, or an inline style attribute. Tokens you leave alone keep their default, so you only declare what you re-brand.

Setting --k-* on an ancestor of the embed does not reach it: the embed declares those tokens on its own root, and a declaration on an element always beats a value inherited from an ancestor. If you want the embed to inherit a token layer your page already publishes at :root, use the web component (<survey-review-workbench>) — its defaults sit on the shadow :host, so host tokens and inline styles propagate through — or restate the tokens on the embed element as below.

/* Your app's own palette drives the workbench — no Kontour branding. */
.survey-workbench-embed {
  --k-brand: var(--brand-accent);
  --k-bg: var(--paper);
  --k-panel: var(--paper-2);
  --k-text: var(--ink);
  --k-line: var(--hairline);
  --k-font-ui: var(--font-body);
  --k-radius-md: 2px;
}

The full token list is in the Consumer Integration Guide. Prefer this over the built-in theme-survey/theme-console/… presets when the host app should read as itself.

Review MCP#

Drive review-queue decisions from any MCP host. The official server runtime automatically supports MCP 2026-07-28 discovery and existing legacy clients:

npx survey-review-mcp --session path/to/session.json

Three tools: survey_review_queue (queue state), survey_review_item (item detail), and survey_review_decide (record a decision). Each queue and item call includes an embedded, fully self-contained review card with Accept / Hold / Reject / Could not confirm actions. See docs/review-mcp.md.

The embedded card is a fixed-size resource (a preferred frame of 420×560) that follows the host's light/dark prefers-color-scheme; it does not resize its layout at other breakpoints. For a card that adapts its layout to available width — stacking the current → proposed diff vertically and growing tap targets via CSS container queries below 620px/480px, with a viewport @media fallback — embed the Review Workbench directly instead.

Server code persisting browser-submitted review events should use persistReviewSessionEvents, then deriveReviewSessionApplyResultForSnapshot (or the composed deriveServerReviewSessionApplyResult 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.

The Consumer Integration Guide covers the full path from ReviewItem construction through persisted review events, exported results, Surface projection, and the full --k-* theming token list. Test-covered examples are under examples/review-workbench/. To run the standalone demo locally, see Review Workbench Prototype.

Portable extraction imports can also attach a source-linked, read-only inspector to the same workbench. It verifies resolved artifact identity before showing exact highlights, keeps unresolved material visibly non-grounded, and exports with source text and excerpts redacted by default. Review queues and inspector candidates use bounded, searchable paging so large validated result sets remain navigable without changing canonical review events or exports. See Extraction Envelope Import.

Standalone review console#

Spawn a loopback browser dashboard backed directly by a session file:

npx survey-review-console --session path/to/session.json [--port 4243]

Opens the full Review Workbench in your browser. Every decision you make is persisted back to the session file atomically via the same deriveServerReviewSessionApplyResult validation the MCP server uses. An SSE stream watches the file for changes, so the browser and any concurrent MCP agent converge live on the same event queue — no page refresh required. See docs/review-console.md.

Where Survey fits#

Kontour AI shows the work behind AI. Survey is the producer-side primitive:

Product Owns
Survey Producer evidence: source → extraction → candidate → review → claim
Surface Portable trust state: claims, evidence, policies, trust snapshots
Flow Process transparency: steps, gates, transitions, runs, exceptions
Veritas Code/change transparency: repo standards, merge readiness
Flow Agents Agent-facing distribution: skills, kits, runtime adapters, hooks

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.

Documentation#

Guide What it covers
Record Contracts every record shape in the chain: raw sources, extractions, candidates, reviews, proofs, resolutions, comfort-zone flags
Adversarial Passes & Learning per-round adversarial review records and learning projections, and the Flow boundary
Consumer Integration Guide the full consumer path: ReviewItem queues, the workbench, persisted events, the server apply boundary
Review Resource Contract the Kontour Resource shapes for review sessions and events
Source-Authority Review Pattern record discipline for sources the producer treats as authoritative
Review Workbench Prototype running the example-backed standalone demo locally
Review Console standalone local dashboard: browser + MCP agent share the session file
Review MCP MCP server for agent-driven review-queue decisions
Releasing release prep and publish flow

Product boundary#

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 evidence chain contract before the records enter Surface.

Contributor checks#

Install the repo-owned Git hooks once per clone:

npm run setup:repo-hooks

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:

npm run validate:repo-hooks
npm run verify

The committed pre-push hook runs both commands from the repo root.

License#

Apache-2.0 © Kontour AI

Documentation

Start Here

Concepts

Guides

Reference

Decisions

Decision IndexGenerated by corpus decisions index. Do not edit by hand. Topic-keyed living decision records; the format is described at https://github.com/kontourai/corpus/blob/main/docs/decisions.md. Numbered ADR… Confidence Calibration from Review OutcomesEvery reviewed candidate in Survey is a labeled calibration sample: the system-proposed candidate carried a stated confidence (the prediction), and the human review either affirmed or overturned that… Choosing One Value of a ConflictAn envelope import groups the proposals for one claim into one ReviewItem with one proposed candidate per distinct value. Two or more values make the set a conflict. Every review decision named a can… Could Not Confirm Review OutcomeA reviewer may make a diligent attempt without obtaining enough evidence to accept, reject, or honestly escalate a candidate. Encoding that non-answer as rejection, verification, or disputed projecti… Reviewed Entity RelationshipsSurvey binds producer-provided alternatives and source evidence to existing review items, decisions and replayable events. It stores immutable, content addressed outcomes and explicit supersession an… Extraction Envelope ImportSurvey consumes the upstream-owned traverse-extraction-result version 1 contract through a structural adapter. It does not define a competing primary extraction envelope and does not add a runtime de… Field States, Carry-Forward and Score-Blind AuditField states are derived, never stored. deriveFieldStates reads import records, decisions, and optionally verifier records, carry-forward records and supersessions, and returns a content state and a… Extraction Improvement ProposalSurvey represents reviewed extraction improvements as immutable, data-only proposals. The proposal references an upstream-owned task by version, digest, and example digests instead of defining or sto… Interpretation RecordIssue #259 asked for two interpretation layers on every verified claim — "gleaned" (what this result taught) and "answer impact" (how it moved the inquiry answer the claim feeds) — producer-side and… Producer ProfileThis subject has provenance in frozen ADR history (0003-inquiry-mapping-and-producer-proposals.md), which established the proposals-only rule this decision continues to honor (ADR 0003 §4: the Produc… Proposed-Value EditHow the Review Workbench offers, carries, and replays a reviewer's inline edit to a proposed Candidate value. Two decisions ratified together in 1.14.0, both surfaced by the first real integration (O… Review WorkbenchThe framework-neutral embeddable review UI and its host-integration contract. Subjects specific to inline editing live in proposed-value-edit; this record carries the embed's theming contract. Reviewed Current/Proposed ResolutionThis subject has provenance in frozen ADR history (0001-reviewed-current-proposed-resolution.md) but no living decision has been ratified yet under the topic-keyed decision registry (format). This st… Reviewer KindA review outcome names its reviewer by actor, a label the producer chose. A host that needs a person to decide certain items, such as routed contradictions in accumulated memory, could not tell from… Source-Linked Extraction InspectorPortable extraction results preserve exact UTF-16 spans and prepared-artifact identity but deliberately exclude prepared text. Reviewers need to inspect those candidates against the resolved source w… Support VerificationSurvey owns a SupportVerifier port and an immutable CandidateVerification record. The record says what an identified verifier said about one candidate's value and evidence. It is not a trust decision… ADR IndexThis index is static: the numbered ADRs are frozen, and Corpus refuses new files under docs/adr/ (npm run check:decisions). Numbered ADRs below are FROZEN immutable history (see the banner on each fi… Reviewed Current/Proposed ResolutionSurvey will treat a reviewed current/proposed decision as a generic producer-side candidate-resolution pattern and provide a reviewedCurrentProposedResolution(...) helper as a specialization of revie… Inquiry Mapping and Producer ProposalsSurvey's source → extraction → candidate → review → claim chain was built for web-crawl and document producers. Two later producer profiles needed to feed the same chain from inputs that are not web…

Contribute

Source: README.md