---
id: STATE-PFM-OPS-002
title: Job step lifecycle
product_area: platform
module: operations
revision: 1
status: draft
object: DOM-PFM-OPS-001
design_intent: proposed
implementation_evidence: observed
source_sha:
  macroservice-libraries: 54536a441935c6e09fdb7f96f0bec71c52ba4e03
states:
  - name: queued
    meaning: 'Submission created the step, with no attempt started.'
  - name: running
    meaning: The current step attempt has begun.
  - name: retrying
    meaning: A transient attempt failure is waiting for another orchestration attempt.
  - name: completed
    meaning: The step has finished successfully and is final.
  - name: skipped
    meaning: The step was not applicable or has been bypassed and is final.
  - name: failed
    meaning: The step has a final unsuccessful outcome.
transitions:
  - from: queued
    to: running
    trigger: The task handler begins the declared step.
    guard: The job is open and the step can be reported.
    side_effects:
      - Append an attempt.
      - Stamp the step’s first start.
      - Update the latest-step summary.
    who: the shared task handler
    provenance: observed
    source: 'macroservice-libraries:packages/jobs/src/task/job-task-handler.ts'
  - from: running
    to: completed
    trigger: The owning task completes successfully.
    guard: The step has no different final outcome.
    side_effects:
      - Close the attempt successfully.
      - Derive progress from settled steps.
    who: the shared task handler
    provenance: observed
    source: 'macroservice-libraries:packages/jobs/src/engine/job-engine.ts:applyStepTransition'
  - from: running
    to: skipped
    trigger: The task declares that the step does not apply.
    guard: The step has no different final outcome.
    side_effects:
      - Recompute earned progress.
    who: the shared task handler
    provenance: observed
    source: 'macroservice-libraries:packages/jobs/src/engine/job-engine.ts:applyStepTransition'
  - from: running
    to: retrying
    trigger: A non-refusal error leaves retries available.
    guard: The job is open and the orchestration retry budget is not exhausted.
    side_effects:
      - Close the attempt with a safe failure account.
      - Retain the previous progress.
    who: the shared task handler
    provenance: test
    source: >-
      macroservice-libraries:packages/jobs/src/workflow/job-workflow.spec.ts#lets no detail of the transient
      error reach the job or the execution state
  - from: retrying
    to: running
    trigger: Orchestration invokes the next attempt.
    guard: The job has not settled before the new attempt.
    side_effects:
      - Append the next attempt.
    who: the shared task handler
    provenance: observed
    source: 'macroservice-libraries:packages/jobs/src/engine/job-engine.ts:deriveAttemptState'
  - from: running
    to: failed
    trigger: 'Failure handling, reconciliation or cancellation closes the active step.'
    guard: The step has not acquired a different final outcome.
    side_effects:
      - Store an operator-safe step explanation.
      - Failure recording alone leaves root settlement to the workflow.
    who: 'the job engine, the reconciler or cancellation'
    provenance: observed
    source: 'macroservice-libraries:packages/jobs/src/engine/job-engine.ts:recordStepFailure'
  - from: retrying
    to: failed
    trigger: Settlement closes a step waiting for another attempt.
    guard: The job remains eligible for settlement.
    side_effects:
      - Store the reconciliation or stop explanation.
    who: the reconciler or cancellation
    provenance: observed
    source: 'macroservice-libraries:packages/jobs/src/engine/job-engine.ts:settleJobFromReconciliation'
  - from: queued
    to: completed
    trigger: Retry creation carries a completed predecessor step into the new job.
    guard: The predecessor is final and the kind permits retry.
    side_effects:
      - Pass the completed key to execution so orchestration bypasses it.
    who: the job engine during retry creation
    provenance: test
    source: >-
      macroservice-libraries:packages/jobs/src/engine/job-engine.spec.ts#answers the steps the prior job
      completed, which the start carries into the new execution
invalid:
  - from: completed
    to: running
    why: A final step rejects a different status; replaying its existing status adds no history.
  - from: failed
    to: retrying
    why: A failed step is final and requires a separate retry job for recovery.
  - from: queued
    to: failed
    why: >-
      Reconciliation does not fail queued steps, although the general reporter does not prohibit that
      transition.
concurrency: >-
  Step and root updates use version checks in a shared transaction. Repeated identical reports add no event,
  while changed final reports are refused. The history retains each recorded attempt.
invariants:
  - INV-DOM-PFM-OPS-001-03
  - INV-DOM-PFM-OPS-001-05
  - INV-DOM-PFM-OPS-001-06
evidence:
  - >-
    macroservice-libraries at 54536a4: persistence/job-persistence-columns.ts (JobStepStatus),
    engine/job-engine.ts (applyStepTransition, recordStepFailure, copyCompletedStepsFromPrior),
    task/job-task-handler.ts, engine/job-step-status-model.spec.ts.
  - >-
    codex examination 2026-10-23: checked the pinned source anchors, planning references and enforcement
    boundaries; unresolved limits are recorded in module findings.
---

# Job step lifecycle

## The Lifecycle

```mermaid
stateDiagram-v2
  [*] --> queued: submission creates step
  queued --> running: attempt starts
  queued --> completed: copied completed work
  running --> completed
  running --> skipped: not applicable
  running --> retrying: retryable attempt error
  retrying --> running: new attempt
  running --> failed: failed or stopped
  retrying --> failed: recovered failure or stop
  completed --> [*]
  skipped --> [*]
  failed --> [*]
```

## Transitions Explained

Each declared step exists at submission. The common task handler reports its attempt, executes the owner’s work and records completion or non-applicability. A retryable error leaves a safe attempt account and freezes progress. A new retry job copies completed predecessor steps so orchestration can bypass them.

## What Cannot Happen

A different final outcome cannot replace a settled step. Identical reports add no history. Reconciliation leaves queued steps alone, but the reporter is not a strict adjacency validator for all nonfinal states. Recording step failure and settling the root are separate engine operations.

## Concurrency and Timing

The writer commits step, event and root summary together using version checks. Transient diagnostic text stays in logs; the recorded attempt can retain an error name and correlation reference.
