---
id: STATE-PFM-DJE-003
title: Upload session
object: DOM-PFM-DJE-003
product_area: platform
module: data-job-engine
revision: 1
status: draft
design_intent: confirmed
origin: >-
  services-platform:docs/proposals/rhythm-data-job-engine-plan.md §6.2 inputs
  (approved 2026-10-12); services-platform:docs/data-jobs/uploads.md
implementation_evidence: observed
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
states:
  - name: open
    meaning: >-
      Signed part transfers are available and the client can complete or abandon
      the upload.
  - name: completed
    meaning: >-
      Storage assembly succeeded and the service saved the object facts needed
      by consumers.
  - name: aborted
    meaning: >-
      The client abandoned the transfer or integrity checks refused its
      assembled object; integrity refusal does not remove that object.
  - name: expired
    meaning: >-
      Storage reported no multipart upload when the service read or completed
      the session; abandonment and an assembly crash can look alike.
transitions:
  - from: open
    to: completed
    trigger: Complete the multipart transfer
    guard: >-
      Each expected part is named once, storage accepts it, and size and any
      checkable digest agree
    side_effects:
      - 'save storage size, version, tag and available verified digest'
    who: Uploader holding the upload permission
    provenance: observed
    source: >-
      services-platform:modules/data-jobs/services/uploads/upload-sessions.ts:completeUpload
  - from: open
    to: aborted
    trigger: Abandon the transfer or refuse integrity
    guard: The session remains open
    side_effects:
      - >-
        explicit abort removes multipart parts before settling; integrity
        failure settles after assembly and retains the object
    who: 'Uploader, or the service refusing a completion'
    provenance: observed
    source: >-
      services-platform:modules/data-jobs/services/uploads/upload-sessions.ts:abortUpload;
      services-platform:modules/data-jobs/services/uploads/upload-sessions.ts:completeUpload
  - from: open
    to: expired
    trigger: Observe a missing multipart transfer
    guard: A read or completion receives NoSuchUpload
    side_effects:
      - record expiry without a further storage effect
    who: 'Platform, on the next operation'
    provenance: observed
    source: >-
      services-platform:modules/data-jobs/services/uploads/upload-sessions.ts:readUploadSession
invalid:
  - from: completed
    to: aborted
    why: 'Completion may already have supplied input to a job, so abort is refused.'
  - from: completed
    to: open
    why: A completed session cannot accept replacement bytes.
  - from: aborted
    to: open
    why: An abandoned transfer requires a fresh session.
  - from: expired
    to: completed
    why: Missing multipart parts cannot be completed through this session.
concurrency: >-
  Storage operations occur without a database transaction, followed by
  row-locked settlement. Repeated abort of the same settled state returns
  unchanged. Missing parts leave completion open for correction. After
  storage-first assembly failure, retry can report expiry instead of repairing
  the session.
evidence:
  - >-
    services-platform at ad3f6a65:
    modules/data-jobs/services/uploads/upload-sessions.ts, upload-storage.ts;
    services/persistence/uploads.ts; db/data-job-uploads.table.ts;
    docs/data-jobs/uploads.md.
---

# Upload Session

## The Lifecycle

```mermaid
stateDiagram-v2
  [*] --> open
  open --> completed: complete the multipart transfer
  open --> aborted: abandon the transfer or refuse integrity
  open --> expired: observe a missing multipart transfer
```

## Transitions Explained

Opening starts storage transfer before persisting the session. Completion assembles the object and checks available integrity evidence before saving success. Explicit abort removes parts first; an integrity refusal only changes the session state after assembly. A missing multipart transfer is recorded as expired on read or completion.

## What Cannot Happen

Settled transfers cannot reopen, and completed sessions refuse abort. A replacement file starts a new session.

## Concurrency and Timing

Part addresses last one hour and are issued in groups of at most 100. Database settlement is locked, but storage and row changes remain separate. A crash between assembly and bookkeeping has no established repair path.
