Skip to content

Typra contracts

Typra keeps TypeSpec as the durable source of truth and treats generated runtime code as a projection that must prove equivalent behavior across languages.

The immediate priority was callable and vector conformance informed by Prompty’s real consumer needs. HTTP transport projection now builds on that foundation rather than driving the contract shape.

Prompty is compatibility evidence, not the authority for Typra’s design. Typra should satisfy Prompty’s durable needs where they align with this contract model, and should push Prompty toward better patterns when current Prompty behavior conflicts with TypeSpec-native contracts, portable vectors, or clean generated/handwritten boundaries. Discovering a better way to build Prompty with Typra is a successful roadmap outcome, even when it requires deliberate contract conversion.

IntentSource of truthTypra role
Data shapeTypeSpec model, scalar, unionGenerate load/save/runtime model surfaces and conformance evidence.
Data examplesTypra @sampleGenerate examples and data conformance tests.
Callable behaviorTypeSpec interface + opLower to callable IR and generate interface/protocol/trait-style surfaces.
Callable runtime effectsTypra operation decoratorsDescribe runtime cancellation, sync signatures, optional operations, and effect metadata TypeSpec does not model.
Cross-runtime behavior casesTypra @vectorDescribe behavior that a callable conformance oracle can execute or simulate.
Transport shapeTypeSpec HTTP decoratorsLayer transport IR on top of callable IR; do not replace TypeSpec HTTP vocabulary.
Runtime-specific outputsTypra output contributorsAdd optional projections without duplicating the core emitter pipeline.
Handwritten boundariesHydration seamsKeep generated signatures stable while runtime behavior remains hand-authored.
Namespace identityTypeSpec namespacesPreserve semantic ownership while each target projects package/module names idiomatically.
  1. Keep data contracts on TypeSpec-native model, scalar, and union declarations. Existing load/save behavior, provider wire mappings, named collections, discriminators, coercions, factories, and parse aliases remain compatibility-critical.
  2. Use TypeSpec interface and op for new callable contracts.
  3. Use operation decorators for runtime-only callable effects: @runtimeCancellable, @sync, @optionalOperation, and @effect.
  4. Keep @sample, @coerce, @entryShorthand, @knownAs, @defaultFor, @factory, and @parseAlias as Typra model/runtime projection decorators because they describe behavior TypeSpec does not directly model for Typra’s current runtimes.
  5. Add @vector only for behavior the callable oracle can observe: inputs, successful results, errors/status, supported effect observations, vector metadata, and target runtime metadata.
  6. Do not introduce Typra-specific HTTP decorators for concepts TypeSpec HTTP already owns. HTTP projection consumes TypeSpec HTTP metadata after callable/vector conformance is proven.
  7. Preserve nested TypeSpec namespaces in the shared IR and structural generated output. Target package, module, and import paths are projections of that semantic identity, not replacements for it.

root-namespace identifies the semantic contract root and root-alias can rename root-owned generated type names. Nested TypeSpec namespaces under that root remain visible to model, callable, vector, transport, export-surface, and cleanup metadata so generated code can explain where a contract came from.

Target renderers use a shared namespace projection model to derive runtime names and physical layout. Structural namespace output is the default: TypeScript and Python emit nested modules/packages, C# and Swift emit nested folders, and Rust emits nested module trees. Java remains package-based and Go remains flat because Go has one package per directory. Per-target namespace, package-name, import-path, and namespace-output: flat options override runtime ergonomics without changing the TypeSpec source of truth.

import "@typra/emitter";
import "@typespec/http";
using TypeSpec.Http;
namespace Typra.Example;
model PromptRequest {
@sample(#{ template: "Hello {{name}}" })
template: string;
@sample(#{ variables: #{ name: "Typra" } })
variables: Record<unknown>;
}
model PromptResult {
instructions: string;
}
const RenderVectors = #[
#{
name: "basic-render",
stage: "render",
portability: "portable",
input: #{ request: #{ template: "Hello {{name}}", variables: #{ name: "Typra" } } },
expected: #{ instructions: "Hello Typra" }
}
];
interface Renderer {
@runtimeCancellable
@vector(RenderVectors)
render(request: PromptRequest): PromptResult;
@sync
summarize(result: PromptResult): string;
}
@route("/render")
interface RenderApi {
@post
render(@body request: PromptRequest): PromptResult;
}

This is the preferred Typra shape: models remain the data contract, interface / op owns callable seams, operation decorators describe runtime-only callable effects, @vector supplies behavior evidence, and TypeSpec HTTP decorators optionally describe transport. Generated targets should keep handwritten runtime logic behind these seams while Typra verifies that observed behavior matches the durable TypeSpec contract.

Typra uses a language-neutral callable-contract IR between TypeSpec and target emitters. The IR records the contract name, operation names, parameter and return types, documentation, grouping, sync/async behavior, runtime cancellation, atomic/non-fatal metadata, source metadata, and the generated/handwritten hydration seam.

TypeSpec-native interface / op declarations lower into this IR before target rendering. That gives Typra one place to generate language-specific protocol, trait, interface, adapter, and vector surfaces. Operation decorators provide the native vocabulary for runtime-only metadata: @optionalOperation, @sync, @runtimeCancellable, and @effect(#{ atomic: true, nonFatal: true }).

@vector attaches operation-level behavior expectations to TypeSpec-native op declarations. A vector can be authored inline or referenced through a TypeSpec constant vector set, and each entry must provide input plus exactly one of expected or expectedError.

Typra lowers vectors into language-neutral callable vector IR with optional metadata such as stage, provider, targetApi, portability, and normalization. Invalid vector shapes produce diagnostics instead of being silently skipped, so vector coverage can become a reliable callable conformance oracle input.

The callable oracle compares normalized transcripts, not target-specific implementation details. Runtime execution or simulation lowers into:

  • vectorId, using Contract.operation:name or Contract.operation:unnamed,
  • target, such as typescript, python, or a future runtime identifier,
  • input, compared structurally as opaque evidence (not normalized through target load/save),
  • result for successful expectations or error for expected error observations,
  • optional effects and metadata when Typra defines observable side-effect semantics.

The oracle succeeds when the observed transcript matches the expected transcript derived from the vector. Failures must include the vector id, target runtime, expected transcript, observed transcript, and the mismatched path so drift is actionable across languages. Generated TypeScript and Python vector conformance tests exercise the transcript shape only: because vector input/expected are opaque conformance evidence (not typed against the operation’s parameters), they are compared structurally and are not round-tripped through model-typed load().save() surfaces. Doing so would force vector authors to pre-normalize inputs into canonical save() form — coupling opaque evidence to a target’s default-serialization rules — and it diverged from the other targets, which never emitted such a round-trip. Model-typed data fidelity is proven by @sample data conformance instead. Handwritten callable invocation can plug into the same transcript once runtime adapters exist.

Typra adds a structural CodeModel between semantic IR and target renderers. The CodeModel records generated surfaces as data before syntax is emitted, so target-specific code can stay focused on language rendering instead of rediscovering import sets, test cases, callable seams, or transport layouts independently.

The first pilot is vector conformance generation. Typra derives one shared vector conformance CodeModel from callable vector snapshots. Vector input/expected are opaque evidence, so the model carries only the serialized snapshot and its JSON constants — it does not derive model-typed load/save roundtrip cases. The TypeScript and Python renderers then consume that same structure to emit target syntax. This keeps the pilot narrow while establishing the pattern future callable adapters and transport projections should follow: semantic IR first, structural CodeModel second, thin target renderer last.

Typra routes optional generated surfaces through internal output contributor requests identified by target, kind, and provider. Core model generation is represented as a models:typra contributor, and existing native-serialization options normalize into native-serialization:<provider> requests for compatibility.

Target configuration can also carry explicit outputs requests for projections. Python supports the first transport contributor with kind: server and provider: fastapi; unsupported target/kind/provider combinations fail with clear diagnostics instead of falling back silently. Public third-party plugin loading remains deferred; the registry is an internal seam so native serialization, callable adapters, and future server/client projections do not grow new one-off top-level options.

Prompty depends on Typra as an agent drift prevention layer, not primarily as an HTTP generator. The guardrail therefore starts with consumer evidence from Prompty, but the preserved contract is Typra’s principle-led interpretation of that evidence:

  • load/save round trips for generated model surfaces,
  • @coerce scalar shorthands and @entryShorthand name-keyed collection entries,
  • explicit named collections such as Record<T> | Named<T>[],
  • discriminated polymorphism,
  • provider wire mappings through @knownAs and @defaultFor,
  • generated @factory helpers,
  • TypeSpec-native callable signatures, operation decorators, and hydration seams,
  • existing load/render/parse vector categories.

Prompty also shows where Typra should be opinionated: callable seams should move to TypeSpec-native interface/op, vectors should become executable conformance evidence rather than ad hoc tests, and host-sensitive behavior such as ${env} / ${file} expansion should remain a hydration seam instead of becoming generic schema defaults. Compatibility shims can protect existing users while Typra points Prompty at a better contract shape.

The compatibility baseline is intentionally transport-neutral. FastAPI and future HTTP projections build on this evidence rather than becoming the first proof of the architecture.

The first consumer-style projection is a non-HTTP Prompty-shaped callable/vector slice. It models runtime seams such as Renderer.render, Parser.parse, Processor.process, and Harness.verify as TypeSpec-native interfaces and operations, uses typed request/response models, records hydration zones for handwritten runtime behavior, and emits protocol scaffolds plus vector conformance tests.

This slice proves the architecture against local callable behavior before transport projection: vectors carry render/parse/process/replay metadata, generated tests compare oracle-style transcripts, and output contributors validate optional surfaces such as native serialization without introducing server/client generation first. HTTP projection only starts after this callable/vector path is stable.

HTTP transport projection consumes TypeSpec HTTP decorators; Typra does not define competing route, verb, parameter, or response decorators. The transport IR layers official @typespec/http metadata on top of callable IR by linking each routed operation back to its TypeSpec-native callable contract.

The IR records the HTTP verb, path, URI template, path/query/header/cookie/body bindings, response status codes, response body type, and response content types. This keeps transport projection downstream of the callable/oracle architecture: target renderers can generate routers or controllers, but model parsing and serialization still route through Typra’s canonical load() / save() semantics.

Python’s HTTP server contributors emit route factories and transport vector tests for configured outputs:

emit-targets:
- type: Python
output-dir: "generated/python/typra/example"
test-dir: "generated/python-tests"
import-path: "typra.example"
outputs:
- kind: server
provider: fastapi
- kind: server
provider: starlette
- kind: consumer
provider: httpx
- type: TypeScript
output-dir: "generated/typescript"
test-dir: "generated/typescript-tests"
import-path: "../typescript/index"
outputs:
- kind: consumer
provider: fetch

The generated FastAPI and Starlette surfaces are adapter seams, not runtime implementations. Handwritten handlers implement the callable behavior; generated routes bind path/query/header/cookie/body values, call model load() for request bodies, call the handler, and return model save() payloads with the TypeSpec-declared success status. Generated transport vector tests use each framework’s TestClient to prove the route projection delivers vector inputs to the handler and preserves expected wire output.

TypeScript’s fetch consumer and Python’s httpx-style consumer are complementary projections. They emit contract-specific clients such as PetsClient or RendererClient backed by an injected transport. Generated methods build URI-template paths, serialize path/query/header/cookie/body bindings, pass TypeSpec auth requirements as metadata, call the injected transport, require a status that matches the declared success response, and hydrate only that status-matched body through generated load() methods. Non-matching 2xx and non-2xx responses both flow through the error seam with the original status/body instead of being loaded as success models. The generated consumer vector tests prove client runtimes can consume the same contract that the Python server projections expose, without making Typra own retries, token acquisition, credential storage, observability, or full SDK policy.