Kontour Surface Snapshot ViewerGitHub
Browse the docs

Authoring a product profile

A product profile is an ordinary SurfaceExtension plus the claims, policies,

and evidence receipts its product already owns. It is not a Surface runtime,

derivation hook, schema field, or new package. Start in the product repository;

extract a package only after a second independent consumer proves that it needs

the same stable vocabulary and policy templates.

examples/answer-provenance.json is the reference bundle. Its

answer-provenance facet has three product-owned claim types:

  • product.answer.llm-answer — a concrete answer-content assertion that may carry semantic support.
  • product.answer.routing-receipt — request-routing context.
  • product.answer.tool-use — tool invocation context.

Routing and tool claims do not entail an answer merely because they share a

request, model, metadata key, or facet. Add declared entailing evidence against

the exact answer claim only when the receipt establishes that answer; otherwise

keep it as context or separate cited evidence.

Register the profile

Use the existing reference factory and registry; metadata is for authoring and

inspection, never status derivation.

import {
  createAnswerAssessmentReferenceExtension,
  registerExtension,
} from "@kontourai/surface";

const reference = createAnswerAssessmentReferenceExtension({
  name: "my-answer-product",
  displayName: "My Answer Product",
  vocab: {},
  theme: { brandName: "My Answer Product" },
});

registerExtension({
  ...reference,
  claimTypes: [
    {
      id: "product.answer.llm-answer",
      displayName: "LLM answer content",
      description: "A concrete content assertion with frozen-source and owner-review backing.",
      defaultImpact: "medium",
      defaultFacet: "answer-provenance",
      policyTemplateId: "product.answer.llm-answer-policy/v1",
      metadataFields: [
        { key: "conversationId", label: "Conversation ID", type: "string" },
        { key: "messageId", label: "Message ID", type: "string" },
        { key: "model", label: "Model", type: "string" },
      ],
    },
    {
      id: "product.answer.routing-receipt",
      displayName: "Routing receipt",
      description: "Request context; it is not answer support.",
      defaultImpact: "low",
      defaultFacet: "answer-provenance",
    },
  ],
  policyTemplates: [
    ...(reference.policyTemplates ?? []),
    {
      id: "product.answer.llm-answer-policy/v1",
      template: {
        claimType: "product.answer.llm-answer",
        requiredEvidence: ["source_excerpt", "human_attestation"],
        requiredMethods: ["observation", "attestation"],
        acceptanceCriteria: [
          "A frozen source excerpt states the exact answer content.",
          "The owner review confirms the content against that frozen source.",
        ],
        reviewAuthority: "answer-product-owner",
        validityRule: { kind: "duration", durationDays: 7 },
        stalenessTriggers: ["frozen source is replaced", "content review is superseded"],
        conflictRules: ["A blocking failed content review disputes the answer claim."],
        impactLevel: "medium",
      },
    },
  ],
});

registerExtension is an authoring/presentation registry only. It cannot make

a claim verified, infer review, or create a semantic relationship.

Positive answer assessment

The owner-built Basis assessment needs a resolved product policy, a verified

nonstale concrete content assertion, and explicitly declared, non-failed

supportStrength: "entails" evidence for that exact answer. Required evidence

types/methods must be met; blocking evidence or gaps prevent success. Cited-only

receipts and failed nonblocking records remain visible but are not positive

support. Rebuild via buildAnswerAssessmentProjection(report, claimId); do not

construct a policy verdict or supply a caller boolean.

The reference profile deliberately uses a frozen source_excerpt plus a

human_attestation for its positive content assertion. Its retained

runtime_observation routing and tool receipts are context claims, not proof of

answer semantics. This is a profile choice, not a global ban on runtime

observations: a runtime fact can support content when the product's own claim

and policy make that direct relation explicit.

const report = buildTrustReport(bundle, {
  id: authorizedImmutableBundleHandle,
  now,
});
const assessment = buildAnswerAssessmentProjection(report, "answer.supported");

The explicit id is an authorized immutable bundle handle. A producer name,

source string, or generated clock value is not an immutable identity.

Interoperability and packaging

Claim types are product vocabulary. Similar answer, routing, or tool claims

from different products do not merge automatically because their metadata or

facets resemble one another. Keep them distinct until a real shared consumer

needs a common contract, then propose an Ops vocabulary convention with

examples from both products. Do not silently promote one product's type into

suite-wide meaning.

requiresCorroboration counts eligible evidence records. A product derivation

condition such as minActors is separate: record count never proves distinct

actors. Keep profile code beside the product's receipt and authorization

boundary. A second consumer sharing stable IDs, policy semantics, and authoring

fields is the evidence needed to extract a profile package.