Effects & reconciliation
How Tarski touches the world — declared capabilities, admission identity, retry policies, the effect lifecycle, work queues, and explicit reconciliation.
Effects are downstream of semantic evaluation, always:
proposal.* → decision.approved.* → intent.* → effect request → attempt → receipt observation
Approved decisions derive intents; effect authority admits requests; the runtime executes declared capabilities; and results return only as receipt observations that re-enter the lineage as evidence. Guest code cannot mutate facts, append observations directly, or use undeclared ambient I/O.
Capabilities
| Capability | Purpose |
|---|---|
http.fetch |
Declared outbound HTTP |
llm.complete |
Explicit model calls for LLM-assisted agents |
blob.put / blob.get |
Content-addressed large payload storage |
timer.schedule |
A future timer observation |
log.dev |
Developer diagnostics only — never canonical state |
Capabilities are declared in tarski.toml and bound to intent relations. Admission
identity is the lineage plus the intent relation name plus declared identity key values —
which is what makes retries and crash recovery resolvable.
If a live binding’s credential reference cannot be resolved, the effect blocks with typed
http.credential.unresolved before the network is opened — no receipt is appended, and
nothing silently substitutes a deterministic provider.
Retries
Idempotent effects may auto-retry when the runtime classifies retry as safe. Non-idempotent or uncertain attempts enter reconciliation instead — policy never overrides the runtime’s safety classification.
Retry policies are declared per intent binding: fixed or exponential backoff, initial and
maximum delays, optional deterministic jitter, and exhaustion bounds (max attempts and/or
elapsed time) with on_exhausted = "failed" or manual reconciliation. Resolved policies
are canonicalized and digested at admission — later manifest edits can never rewrite the
policy an old decision ran under.
The effect lifecycle family
The runtime populates a reserved relation family your rules can read (never declare):
effect.queued, effect.in_flight, effect.succeeded, typed effect.failed,
effect.blocked, effect.reconcile_required, effect.conflict, plus grouping views
effect.open and effect.terminal.
Rules that derive an intent at head H can read that effect’s lifecycle evidence only at a later head — same-head feedback loops are structurally impossible.
Work queues
Work queues are operational dispatch control for already-admitted requests — “when may this start?”, never “may this happen?”. Queue policy (concurrency, partitions, ordering, lease timing, rate limits, saturation behavior) is frozen at admission with its own digest; operators adjust capacity by appending durable queue-control records, not by editing manifests. Inspect with:
tarski work-queue list
tarski work-queue inspect
Reconciliation
When an effect attempt is ambiguous — a crash mid-flight, an uncertain provider — the
runtime never silently guesses. The attempt parks as reconcile_required, and an
operator resolves it explicitly:
tarski reconcile inspect --session latest
tarski reconcile resolve <attempt-id> --outcome <succeeded|failed|retry>
The resolution appends as an observation, so the decision is part of the lineage — auditable, replayable, and visible in provenance like everything else. Manual retry keeps the original attempt counter and frozen policy metadata.
Typed HTTP receipts
Plugin-owned http.fetch bindings may declare a receipt_kind plus a request template,
producing typed, schema-validated receipt observations (e.g. a product-page fetch result)
instead of the generic effect.result. App-owned HTTP bindings emit generic
effect.result receipts in V1. result_kind belongs to llm.complete only. See
Plugins.