<!-- ephemeral -->

# TeaQL TypeScript Runtime Customization

The application composition root owns the provider, request policy, tenant and App audit sink.
Never deserialize those resources from request or federation JSON. The following code is the
executable baseline; import generated Request/Model types from the generated workspace.

```typescript
import { UserContext } from "@teaql/teaql";
import { TeaQLDataService } from "@teaql/teaql/sql/core";

export interface AppAuditEvent { readonly [key: string]: unknown }
export type AppAuditSink = (event: AppAuditEvent) => void | Promise<void>;
export interface RequestPolicy {
  readonly authorize: (operation: string, entity: string) => boolean;
}

export interface RuntimeDataService extends TeaQLDataService {
  setAuditSink(sink: AppAuditSink): this;
}

export interface RuntimeComposition {
  readonly dataService: RuntimeDataService;
  readonly requestPolicy: RequestPolicy;
  readonly appAuditSink: AppAuditSink;
  readonly trustedTenant: string;
}

export function requestContext(composition: RuntimeComposition): UserContext {
  if (!composition.trustedTenant.trim()) {
    throw new Error("A trusted tenant is required");
  }
  composition.dataService.setAuditSink(composition.appAuditSink);
  return new UserContext()
    .insertResource("dataService", composition.dataService)
    .insertResource("requestPolicy", composition.requestPolicy)
    .insertResource("appAuditSink", composition.appAuditSink)
    .insertResource("trustedTenant", composition.trustedTenant);
}

export async function readiness(context: UserContext): Promise<void> {
  context.requireResource<RequestPolicy>("requestPolicy");
  context.requireResource<AppAuditSink>("appAuditSink");
  if (!context.requireResource<string>("trustedTenant").trim()) {
    throw new Error("A trusted tenant is required");
  }
  await context.ensureSchema();
}

const GOVERNANCE_KEYS = new Set([
  "tenant", "trustedTenant", "provider", "dataService", "requestPolicy",
  "auditSink", "appAuditSink", "hardLimit", "continuousPage",
]);

export function rejectGovernanceOverride(value: unknown): void {
  if (Array.isArray(value)) {
    value.forEach(rejectGovernanceOverride);
    return;
  }
  if (value === null || typeof value !== "object") return;
  for (const [key, nested] of Object.entries(value as { [key: string]: unknown })) {
    if (GOVERNANCE_KEYS.has(key)) {
      throw new Error(`Untrusted governance override: ${key}`);
    }
    rejectGovernanceOverride(nested);
  }
}
```

Generated query and mutation code receives only this trusted `UserContext`. Keep `.comment(...)`,
`.purpose(...)`, and mutation `.auditAs(...)` non-empty. Readiness must reach the real provider;
an HTTP-only health response is not sufficient. Browser/federation payloads may contain allow-listed
business query fields, but never the resources rejected above.

## Runtime telemetry

Observability is optional and application-owned. Construct
`OpenTelemetryRuntimeTelemetry` from `teaql-ts/telemetry/opentelemetry` with the
application's tracer, meter, logger and lifecycle delegates, then call
`dataService.setRuntimeTelemetry(telemetry)` (and the same method on a TFP
client when used). Keep the no-op default when absent. The application owns
bounded processors, OTLP exporters, `flush()` and `shutdown()`; exporter failure
must never change business results. Telemetry setup does not call
`ensureSchema()`.
TeaQL derives `teaql.error.category` from the native error type. Sampling never
controls or replaces App Audit Sink delivery. Do not generate a Collector,
additional exporters, auto-discovery, or a telemetry configuration DSL.

---

## TeaQL seven-language assist contract

Apply the verified Rust semantic ceiling while using only the exact TYPESCRIPT generated and
runtime APIs. Discover APIs through the generated application AGENTS.md and progressive
model-aware Assist. Do not inspect generated domain-library source.

- Do not create plurals by appending `s` or `es`; use the centralized generated plural.
- Human and non-human entities use different generated predicate vocabularies. Preserve
  forms such as “who are active” and “whose email is”; never infer them from English.
- Configure filters, projection, paging, and other query options before `purpose(...)`.
  Comment may appear anywhere in the chain. Purpose enters the executable stage; execution
  requires both values, but comment does not have to immediately precede purpose.
- Every execute/list/stream and every save accepts exactly one context argument:
  `UserContext`. Name that argument `context`, never `runtime`; data services and global
  policy are injected when the context is built. Reserve `runtime` for process-level
  runtime ownership, provider/pool setup, and module assembly.
- Tenant, merchant, identity, permissions, request policy, purpose policy, hard limit,
  and continuous-page cursor policy come only from trusted context, never dynamic JSON or TFP.
- If the required operation is absent after current entity/action and required field
  Assist, stop that path and report MISSING_ASSIST. Do not guess an API or search the
  generated library as a fallback.
- Create each application-owned source file once. After its first compile attempt,
  repair only the smallest block identified by the exact compiler or test diagnostic.
  Preserve unrelated code; do not rewrite the complete file as an error-recovery loop.
- Before a repair that would replace more than 25% of an existing application file,
  stop and report LARGE_REWRITE_REQUEST with the file, exact diagnostic, reason, and
  estimated scope. Initial creation and model-driven regeneration are not repairs.

Capability: `runtime-custom`.

- Keep trusted dependencies and global runtime policy in UserContext initialization.
  Custom providers, policy hooks, and audit sinks must not add execute/save arguments.
- Preserve immutable row audit events and a separate customizable App Audit Sink.
  Include health, integration, and negative governance tests for every customization.
