---
id: STATE-PFM-LLM-001
title: AI request lifecycle
object: DOM-PFM-LLM-001
product_area: platform
module: llm
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
states:
  - name: estimated
    meaning: The runtime has written an estimate; admission and dispatch have not been recorded.
  - name: reserved
    meaning: The request records its held credits and running slot.
  - name: dispatched
    meaning: Dispatch is recorded; provider construction and contact may still be pending.
  - name: completed
    meaning: The loop returned a nonfailure outcome and recorded usage and counts.
  - name: failed
    meaning: The loop returned a provider or execution failure with a closed classification.
  - name: pending
    meaning: No usage was reported for a completed loop result.
  - name: settled
    meaning: The request has terminal settlement bookkeeping; period accounting can still be incomplete.
  - name: refused
    meaning: Admission recorded a terminal refusal with no provider dispatch.
transitions:
  - from: estimated
    to: refused
    trigger: The access check rejects the AI or operation switch.
    guard: Current status is estimated.
    side_effects:
      - >-
        Persist the refusal reason and snapshot; create the month if needed without reserving credits; raise
        the disabled-operation error.
    who: 'LLM foundation runtime, on the consumer''s request'
    provenance: observed
    source: 'services-platform:modules/llm/services/execution/run-operation.ts:runOperation'
  - from: estimated
    to: refused
    trigger: Admission cannot fit the reservation or allocate a running slot.
    guard: Current status is estimated.
    side_effects:
      - >-
        Persist the admission snapshot and closed reason; allowance takes precedence over concurrency, whose
        response carries a two-second retry hint.
    who: LLM foundation runtime
    provenance: observed
    source: 'services-platform:modules/llm/services/execution/run-operation.ts:runOperation'
  - from: estimated
    to: reserved
    trigger: Admission successfully reserves credits and a slot.
    guard: Current status is estimated.
    side_effects:
      - Record the reserved amount after the separate period movement.
    who: LLM foundation runtime
    provenance: observed
    source: 'services-platform:modules/llm/services/persistence/invocations.ts:markInvocationReserved'
  - from: reserved
    to: dispatched
    trigger: Request capture has returned and its metadata was written.
    guard: Current status is reserved.
    side_effects:
      - Stamp dispatch after retaining capture status and any object reference.
    who: LLM foundation runtime
    provenance: observed
    source: 'services-platform:modules/llm/services/persistence/invocations.ts:markInvocationDispatched'
  - from: dispatched
    to: completed
    trigger: The loop returns an outcome other than failed.
    guard: Current status is dispatched.
    side_effects:
      - >-
        Record outcome, usage, unpriced units, turn and tool counts and completion time after response
        capture.
    who: LLM foundation runtime
    provenance: observed
    source: 'services-platform:modules/llm/services/persistence/invocations.ts:markInvocationCompleted'
  - from: dispatched
    to: failed
    trigger: The loop reports failure after provider execution or attempt recording.
    guard: Current status is dispatched.
    side_effects:
      - 'Store the mapped timeout, unavailability or exhausted-account classification and available usage.'
    who: LLM foundation runtime
    provenance: observed
    source: 'services-platform:modules/llm/services/persistence/invocations.ts:markInvocationFailed'
  - from: completed
    to: pending
    trigger: A completed loop result contains no reported usage.
    guard: Current status is completed.
    side_effects:
      - >-
        Record pending before the immediate runtime settlement; a process interruption can leave recovery to
        close it.
    who: LLM foundation runtime
    provenance: observed
    source: 'services-platform:modules/llm/services/persistence/invocations.ts:markInvocationPending'
  - from: completed
    to: settled
    trigger: Runtime accounting closes a completed result.
    guard: Current status is one of the unsettled statuses.
    side_effects:
      - >-
        Apply actual usage or zero release, increment task totals atomically with the request, then move the
        period separately and evaluate thresholds; provider refusal is raised afterward.
    who: LLM foundation runtime
    provenance: observed
    source: 'services-platform:modules/llm/services/execution/run-operation.ts:settleInvocation'
  - from: pending
    to: settled
    trigger: Runtime or stale-request recovery closes an unmeasured result.
    guard: Current status is one of the unsettled statuses.
    side_effects:
      - >-
        Runtime charges the estimate only for a successful unmeasured attempt and otherwise releases; recovery
        charges the estimate, then separately moves the period.
    who: 'LLM foundation runtime, or the hourly reconciliation function'
    provenance: observed
    source: 'services-platform:modules/llm/services/execution/run-operation.ts:settleInvocation'
  - from: failed
    to: settled
    trigger: Runtime accounting closes a failed loop result.
    guard: Current status is one of the unsettled statuses.
    side_effects:
      - >-
        Use positive reported usage, a successful unmeasured attempt’s estimate or a zero release; return the
        closed provider error after accounting.
    who: LLM foundation runtime
    provenance: observed
    source: 'services-platform:modules/llm/services/execution/run-operation.ts:settleInvocation'
  - from: dispatched
    to: settled
    trigger: An exception escapes the protected execution block.
    guard: The local settled flag is false; the settlement write accepts unsettled statuses.
    side_effects:
      - >-
        Attempt zero-cost settlement and then release the estimated hold separately; cleanup errors can
        supersede the earlier error.
    who: LLM foundation runtime (its cleanup block)
    provenance: observed
    source: 'services-platform:modules/llm/services/execution/run-operation.ts:runOperation'
  - from: estimated
    to: settled
    trigger: Recovery selects a stale estimated request.
    guard: The request is unsettled and started more than twenty-four hours earlier.
    side_effects:
      - >-
        Charge the estimate without cents, remove the recorded hold and a running slot with floors at zero,
        and stamp a past-window alert.
    who: Hourly reconciliation function
    provenance: observed
    source: 'services-platform:modules/llm/services/reconciliation/llm-settlement-sweep.ts:sweepUnsettledInvocations'
  - from: reserved
    to: settled
    trigger: Recovery selects a stale reserved request.
    guard: The request is unsettled and started more than twenty-four hours earlier.
    side_effects:
      - 'Charge the estimate without cents, replace the recorded hold and stamp the recovery alert.'
    who: Hourly reconciliation function
    provenance: observed
    source: 'services-platform:modules/llm/services/reconciliation/llm-settlement-sweep.ts:sweepUnsettledInvocations'
  - from: dispatched
    to: settled
    trigger: Recovery selects a stale dispatched request.
    guard: The request is unsettled and started more than twenty-four hours earlier.
    side_effects:
      - Charge the estimate independently of persisted attempt usage and stamp the recovery alert.
    who: Hourly reconciliation function
    provenance: test
    source: >-
      services-platform:modules/llm/tests/integration/services/reconciliation/llm-settlement-sweep.integration.spec.js#settles
      it at its estimate, because its real cost was never learned
invalid:
  - from: settled
    to: settled
    why: >-
      A repeated settlement is a no-op on request and task totals; runtime period cleanup is a separate
      concern.
  - from: refused
    to: settled
    why: Refusal is excluded from settlement and stale-request selection.
  - from: settled
    to: dispatched
    why: Normal dispatch requires reserved status and cannot reopen a terminal request.
  - from: estimated
    to: dispatched
    why: The dispatch write matches only reserved requests.
  - from: completed
    to: failed
    why: Failure recording expects dispatched status rather than completed.
  - from: refused
    to: reserved
    why: Admission creates a fresh request instead of reopening a refusal.
concurrency: >-
  Status comparison allows only one settlement write to apply. Hold recording and period charging remain
  separate transactions, so losing or interrupted writers can leave inconsistent accounting. Recovery selects
  work older than a day; the running-request cap controls ordinary concurrent admission.
invariants:
  - INV-DOM-PFM-LLM-001-01
  - INV-DOM-PFM-LLM-001-02
  - INV-DOM-PFM-LLM-001-03
  - INV-DOM-PFM-LLM-001-04
  - INV-DOM-PFM-LLM-001-05
evidence:
  - >-
    services-platform at ad3f6a6: modules/llm/db/llm-invocations.table.ts (LLM_INVOCATION_STATUSES,
    LLM_INVOCATION_TRANSITIONS); services/persistence/invocations.ts; services/execution/run-operation.ts;
    services/reconciliation/llm-settlement-sweep.ts; tests/unit/db/invocation-transitions.spec.ts;
    tests/integration/services/reconciliation/llm-settlement-sweep.integration.spec.js.
---

# AI request lifecycle

## The Lifecycle

```mermaid
stateDiagram-v2
  estimated --> refused
  estimated --> reserved
  reserved --> dispatched
  dispatched --> completed
  dispatched --> failed
  completed --> pending
  completed --> settled
  pending --> settled
  failed --> settled
  dispatched --> settled
  estimated --> settled
  reserved --> settled
```

## Transitions Explained

The runtime writes estimated before checking switches and reserving capacity. Successful admission is followed by reserved and dispatched bookkeeping. The loop records a completed or failed result; unmeasured completion briefly passes through pending. Settlement and task totals share a transaction, followed by a separate allowance movement. Exceptions inside the protected block attempt a release, while process death and earlier failures wait for stale-request recovery.

## What Cannot Happen

Settled and refused requests cannot undergo another ordinary status transition or settlement. This applies to accounting status, not every metadata field: attempt, capture and recovery-alert writers do not all require an unsettled status.

## Concurrency and Timing

Admission, hold recording, request settlement and period movement are separate failure boundaries. Recovery charges stale requests at their estimate after twenty-four hours. A terminal request with incomplete period accounting is excluded from that sweep.
