Review Resource Contract#

Survey review resources are producer-neutral envelopes for UI prototypes and adapter tests. They sit beside the existing record contract and keep Survey out of producer policy, queue state, reviewer form state, and product-specific field catalogs.

Each resource uses a Kubernetes-inspired shape:

{
  apiVersion: "survey.kontourai.io/v1alpha1",
  kind: "ReviewItem" | "ReviewDecision" | "ReviewSession" | "ReviewSessionEvent",
  metadata: { name, uid?, labels?, annotations?, producer? },
  spec: { ...producer-declared intent },
  status: { ...Survey-readable observation hints }
}

Resources#

ReviewItem describes one reviewable target and its candidates. It carries source references, locator or excerpt context, extraction confidence, candidate roles, claim target hints, and optional projection hints for Survey records. Roles include current and proposed, but also neutral roles such as alternative, source-version, and computed so regulated-document reviews do not have to pretend every comparison is a current/proposed pair.

ReviewCandidate is embedded inside ReviewItem. It is intentionally not a top-level resource because candidate lifecycle belongs to the producer and the portable contract only needs the serializable candidate payload.

ReviewDecision describes the reviewer decision for a ReviewItem: candidate, status, actor, reviewed time, rationale, evidence ids, comfort-zone notes, and projection hints.

ReviewSession is a portable envelope for one review session. It carries the session snapshot hash, item count, event count, and status so downstream systems can validate replay without importing workbench mechanics. A session does not own queue position, assignment, retry, lock, or workflow state — those remain in the producer's own system.

ReviewSessionEvent is a single event in a review session, such as a decision, undo, or session-complete marker. Events are replayed against the session snapshot to derive the final apply result. See consumer-integration-guide.md for the full replay and apply boundary.

Ownership#

Producers own acquisition, parsing, candidate ranking, review UX, vertical policy, field catalogs, reviewer assignment, and operational state. Survey owns the portable source, extraction, candidate, review, claim target, and projection record shapes needed to build a Surface Trust Bundle.

Field ownership:

Field area Owner Notes
metadata.name, labels, annotations producer Stable producer identity and grouping labels.
metadata.producer, spec.producerPolicy, candidate.producer producer Domain context and policy hints; Survey treats these as opaque data.
candidate.source producer declares, Survey maps Maps to RawSource when an adapter emits Survey records.
candidate.locator, candidate.extraction producer declares, Survey maps Maps to Extraction, including locator, excerpt, confidence, extractor, and extracted time.
candidate.role, spec.selectedCandidateId producer declares Survey does not enforce current/proposed-only policy.
candidate.rejectionReason producer declares Optional rationale for a candidate the producer already treats as non-selected, superseded, or rejected; Survey records it without ranking candidates or defining rejection policy. A rejection reason is not a comfort-zone signal by itself.
candidate.claimTarget shared boundary Producer identifies the desired Surface claim target; Survey preserves compatible ClaimTarget fields.
ReviewDecision.spec producer reviewer event Maps to ReviewOutcome without bringing producer queues into Survey.
projection hints Survey-readable Optional ids linking resources to RawSource, Extraction, CandidateSet, ReviewOutcome, and ClaimTarget records.

Mapping To Survey Records#

Resource field Survey record
ReviewCandidate.source.sourceRef, kind, observedAt, checksum, locatorScheme RawSource
ReviewCandidate.extraction.target, confidence, extractor, extractedAt plus locator Extraction
ReviewItem.spec.target, candidates, selectedCandidateId, candidateSetStatus, rationale, candidate.rejectionReason CandidateSet and Candidate
ReviewDecision.spec.status, actor, reviewedAt, rationale, evidenceIds, withinComfortZone ReviewOutcome
ReviewCandidate.claimTarget ClaimTarget
projection Optional id bridge for tests and adapters
ReviewSession.spec.snapshot, itemCount, eventCount Replay envelope for session validation
ReviewSessionEvent.spec.type, reviewItemName, candidateId, status, actor Replayable event log for derive/apply

Session resource mapping and the snapshot-safe replay/export helpers are covered in detail in consumer-integration-guide.md.

Adapters should emit normal SurveyInput records and then call buildSurveyTrustBundle. Review resources are a durable neutral contract for review payloads, not a second Surface projection path.

Rejected candidates and comfort-zone review posture are separate signals. Ordinary rejected-candidate feedback should stay on the candidate/review record and, when Survey supports it, project as rejected-candidate learning. It should not be modeled as withinComfortZone: false just to produce learning.comfort-zone. Use withinComfortZone: false only when the reviewer explicitly records that the conclusion is outside their authority or domain comfort and needs a different authority to confirm.

Examples#

The public-directory example demonstrates a current/proposed field review. The regulated-document example demonstrates multi-candidate source-version and computed roles without requiring current/proposed semantics. Both examples are plain serializable TypeScript objects and avoid private downstream product names.

Prototype#

See review-workbench-prototype.md for the example-backed browser prototype that renders a browser-safe copy of the public-directory ReviewItem, guarded against drift from the canonical example, and emits local in-memory ReviewDecision payloads for accept proposed, keep current, and reject proposed decisions.

See consumer-integration-guide.md for the recommended consumer path from ReviewItem construction through persisted review events, exported results, and optional Surface projection. A generic review adapter builder is deliberately deferred until another producer proof shows repeated, policy-free Survey-shape friction.

Collection provenance#

A ReviewDecision can carry an authorizing block inside its spec (mapped from ReviewOutcome.authorizing). This block records how the reviewer was asked and what action they took — the testimony provenance that makes a decision self-contained for downstream admissibility checks.

Three kinds are admissible: explicit-statement (reviewer typed a free-form statement), exchange (a prompt was shown and the reviewer responded — both halves required), and authorized-action (reviewer clicked a named action against a versioned prompt; requires promptRef, renderedPrompt, action, and authorityRef).

Vertical UIs inherit correct collection by using the workbench. buildReviewDecision now populates authorizing automatically on every workbench decision. The block kind is authorized-action with:

  • promptRef: "review-workbench/decision-card@v1" — a stable versioned identifier for the decision card control.
  • renderedPrompt: the review question rendered for that item, including the target label and both candidate values, so the block is self-contained.
  • action: "affirmed-control" for a pure button click, "typed" when the reviewer also supplied a rationale note.
  • authorityRef: "actor:<actorId>" — the actor identity already on the outcome.

The provenance logic lives in the workbench boundary, not in the vertical UI, so consumer products do not need to re-implement it. The authorizing field is optional; existing records without it remain valid.

If buildAuthorizedActionAuthorizing returns an invalid block (e.g., an empty actorId during testing), the workbench records the outcome without authorizing and emits a console.warn. This is a transparency gap, not a hard block, per ADR 0004.

For consumers building outcomes outside the workbench, buildAuthorizedActionAuthorizing is exported from @kontourai/survey. It constructs and validates the block, throwing on invalid inputs so callers catch configuration errors at build time.

Validation is available via validateAuthorizing(block) from @kontourai/survey. It returns structured issues for transparency-gap reporting; it does not hard-block decisions. Gaps are flagged for human review, never silently resolved by model judgment.