engineering

From Model Output to Deterministic Preflight

A proposal is only the beginning: Mowazi tests the current world again before asking an owner to sign.

Kiyan EngineeringAug 9, 20265 min read
Editorial artwork for From Model Output to Deterministic Preflight

Financial automation is vulnerable to a simple error: treating an earlier conclusion as though it were still valid when it is time to act. Markets move, balances change, venues degrade, and a previously acceptable route can become inappropriate.

Mowazi therefore treats model output as a proposal that must be checked against the current world.

The path to an action

The system moves through six distinct stages:

  1. A user-defined policy establishes the mandate and limits.
  2. Specialists and synthesis produce structured evidence and a proposal.
  3. Deterministic services retrieve current inputs and validate the proposal.
  4. The execution boundary checks balance, policy, freshness, health, quorum, and reconciliation conditions.
  5. An owner-controlled signing flow decides whether to authorize the prepared action.
  6. A venue adapter handles the narrowly scoped execution work.

Each stage has a different job. Keeping them separate makes retries safer and failure states more understandable.

Why preflight must be deterministic

An LLM can help reason over an ambiguous situation. It should not decide whether a quote is fresh enough, whether a limit is exceeded, or whether a signer has approved the exact action being prepared. Those are deterministic questions, and the answers should be testable without asking a model to remember its earlier reasoning.

This distinction is especially important when an application is developing toward broader capability. A reliable preflight path is not a marketing flourish. It is the mechanism that keeps analysis from turning into unreviewed execution.

A current proposal is still an old observation

Mowazi's implementation treats analysis time and execution time as different moments. An analysis job can assemble evidence, select a candidate venue, and create a proposal. Between that result and any action, the market can change, a venue can lose readiness, an account can be altered, or an operator can enable an emergency stop. None of those changes invalidate the usefulness of the analysis; they invalidate the assumption that its result is still eligible to act.

The execution workflow therefore builds a fresh risk context rather than trusting the analysis record alone. It includes the original analysis timestamp and current facts such as evidence completeness, emergency-stop state, model quorum, venue health, and reconciliation readiness.

# Simplified from Mowazi's execution workflow.
risk = RiskContext(
    now=current_time,
    analysis_created_at=analysis_time,
    confidence=proposal.confidence,
    consensus=proposal.consensus,
    emergency_stop=strategy.get("emergencyStop", False),
    evidence_complete=bool(proposal.evidence_ids),
    provider_quorum=True,
    venue_healthy=False,
    reconciliation_clear=False,
)

The conservative defaults are important. A field without an authoritative current owner should not be assumed healthy merely because the earlier proposal looked reasonable. Unknown must reduce authority.

01 / proposal

Candidate route

An earlier analysis selects a venue and size.

02 / fresh check

Quote + safety

The gateway requotes and records scoped safety decisions.

03 / dispatch

Allow or block

A failed deterministic check stops the route before submission.

A fresh quote and deterministic policy checks happen after a proposal exists, immediately before dispatch.

Two checks, then a decision

The current workflow calls /internal/preflight/admission before /internal/preflight. Admission binds the request to its scoped observations, idempotency key, subject key, and market/venue relationship. Preflight then requests a fresh executable quote and evaluates the deterministic execution policy against that request.

admission = await gateway_call(
    "/internal/preflight/admission",
    safety_payload,
)

checked = await gateway_call("/internal/preflight", {
    **safety_payload,
    "safety_evaluated_at": fresh_time.isoformat(),
    "admission_decision": admission["preflightAdmissionDecision"],
})

quote = checked["quote"]
decision = checked["decision"]

This does not make a claim that every possible venue is executable. It ensures that the path which does ask for execution must ask the relevant question again at the point of consequence. The implementation records both preflight and submission safety decisions in its trace, including decision hashes and bounded receipt references. An operator inspecting a blocked route can see which stage declined it.

Freshness is a policy input, not presentation copy

The Mowazi venue architecture is particularly strict about this for Arbitrum routes. Its Uniswap flow includes approval checking, quote preparation, typed-data signing where required, transaction validation, eth_call simulation, policy and gate checks, broadcast, and receipt reconciliation. Quotes older than thirty seconds are rejected in that documented route. The value itself can change as implementation evolves; the pattern should not: a cache can improve response time, but it cannot prove that an execution request is still allowed.

Redis is therefore used for short-lived routing previews and coordination, while Convex owns current product projections and Timescale holds detailed evidence and history. A cached preview is a performance hint. It is never an authorization artifact.

Deterministic checks are designed for tests

The deterministic step should be testable by constructing a proposal and current context, not by asking a model whether it feels safe. A minimal policy evaluation can be expressed as ordinary code:

type Preflight = {
  quoteAgeSeconds: number;
  emergencyStop: boolean;
  venueHealthy: boolean;
  approvalMatchesRequest: boolean;
};

export function canDispatch(input: Preflight) {
  if (input.emergencyStop) return { result: "block", reason: "emergency_stop" };
  if (!input.venueHealthy) return { result: "block", reason: "venue_unhealthy" };
  if (input.quoteAgeSeconds > 30) return { result: "block", reason: "stale_quote" };
  if (!input.approvalMatchesRequest) return { result: "block", reason: "approval_mismatch" };
  return { result: "pass" as const };
}

This example is intentionally smaller than the production policy surface. The point is that each reason is explicit, reproducible, and available for audit. A model can help produce the proposal that reaches this function. It must not be the function.

What happens after a pass

Passing preflight does not erase uncertainty. The execution boundary dispatches the scoped request with the same idempotency key. If the downstream result is submitted, executed, uncertain, or pending, the workflow starts a separate reconciliation preflight and reconciliation call. This prevents an ambiguous response from being described as a completed financial result.

That is the engineering value of preflight: it produces a narrow, time-bound decision, and it leaves a trail when the world changes again.