Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

244 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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 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:

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

Quickstart

npm install @kontourai/survey @kontourai/surface

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

About

Producer-side fact-review records — sources, observations, extractions, candidates, and reviews turned into Surface-ready trust claims, without absorbing your domain policy.

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages