---
id: STATE-PFM-LLM-002
title: AI task execution 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: absent
    meaning: No task record has been created for this work or replay key.
  - name: running
    meaning: The task is open and can accumulate request settlements.
  - name: completed
    meaning: The work returned and its close recorded a completion time and duration.
  - name: failed
    meaning: The work threw and its close retained the accumulated charges.
  - name: abandoned
    meaning: Recovery closed stale running work at detection time without a measured duration.
transitions:
  - from: absent
    to: running
    trigger: A consumer opens a declared task.
    guard: 'The context identifies an organization, the task key is declared and no execution is already attached.'
    side_effects:
      - Commit the task before invoking work on its scoped context.
    who: The consuming feature (today Queries' generation worker) through the execution wrapper
    provenance: observed
    source: 'services-platform:modules/llm/services/execution/with-llm-execution.ts:withLlmExecution'
  - from: running
    to: completed
    trigger: The task body returns successfully.
    guard: Current task status is running.
    side_effects:
      - Stamp end and duration; expose a close failure to the consumer.
    who: Execution wrapper
    provenance: observed
    source: 'services-platform:modules/llm/services/persistence/executions.ts:closeExecution'
  - from: running
    to: failed
    trigger: The task body throws an error.
    guard: Current task status is running.
    side_effects:
      - Attempt a failed close; log close errors and preserve the task’s original error.
    who: Execution wrapper
    provenance: observed
    source: 'services-platform:modules/llm/services/execution/with-llm-execution.ts:withLlmExecution'
  - from: running
    to: abandoned
    trigger: Recovery selects a task still running after a day.
    guard: The close still matches running status.
    side_effects:
      - 'Record abandonment time without duration, retaining totals and logging the task.'
    who: Hourly reconciliation function
    provenance: observed
    source: 'services-platform:modules/llm/services/persistence/executions.ts:markExecutionAbandoned'
  - from: completed
    to: completed
    trigger: A request settles after its enclosing task completed.
    guard: The request refers to the task and its settlement applies.
    side_effects:
      - 'Atomically increment task request count, credits and cents without reopening its status.'
    who: LLM foundation runtime or recovery
    provenance: observed
    source: 'services-platform:modules/llm/services/persistence/executions.ts:addInvocationToExecution'
invalid:
  - from: completed
    to: running
    why: Closed tasks cannot be reopened by the closing writer.
  - from: failed
    to: completed
    why: A failed close cannot be replaced by a later successful close.
  - from: abandoned
    to: completed
    why: An abandoned status prevents a late completed close.
  - from: running
    to: running
    why: >-
      Opening the same replay key returns the earlier task without executing work again; an unkeyed open
      creates another task.
concurrency: >-
  An organization-wide unique replay key makes duplicate opens return the same task. Close and abandonment
  compare running status; only one wins. Settlement increments are atomic and remain allowed after closure.
  Every hourly sweep checks closed-task totals over a seven-day window.
invariants:
  - INV-DOM-PFM-LLM-001-07
  - INV-DOM-PFM-LLM-001-10
evidence:
  - >-
    services-platform at ad3f6a6: modules/llm/services/execution/with-llm-execution.ts, execution-scope.ts;
    services/persistence/executions.ts; db/llm-executions.table.ts;
    services/reconciliation/llm-settlement-sweep.ts;
    tests/integration/lifecycle/executions.integration.spec.js.
---

# AI task execution lifecycle

## The Lifecycle

```mermaid
stateDiagram-v2
  absent --> running
  running --> completed
  running --> failed
  running --> abandoned
  completed --> completed
```

## Transitions Explained

The wrapper creates running work and supplies a context that carries its task identity. It closes on return or exception, while process interruption leaves recovery to record abandonment. Late request settlements can continue to change totals for completed, failed or abandoned tasks.

## What Cannot Happen

A closed task is never reopened by the wrapper. Replaying an existing key does not rerun its work. Nesting task wrappers is rejected, and task status is independent of whether its underlying domain work was persisted.

## Concurrency and Timing

Task creation commits before work begins. A successful body followed by a failed close still returns an error; an unsuccessful body preserves its original error even if closing fails. Recovery abandonment records detection time, not the unknown instant the process stopped.
