---
id: STATE-PFM-INT-013
title: Extension binding lifecycle
object: DOM-PFM-INT-005
product_area: platform
module: integrations
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
states:
  - name: draft
    meaning: A saved attachment is still being prepared and takes no part in resolution.
  - name: enabled
    meaning: The attachment can supply the applied handler result.
  - name: shadow
    meaning: The attachment participates in shadow execution while its answer is not applied.
  - name: disabled
    meaning: The attachment is retained but excluded from resolution.
  - name: orphaned
    meaning: 'Registration withdrew its point, leaving the attachment excluded until administrative handling.'
transitions:
  - from: draft
    to: enabled
    trigger: Apply a prepared attachment
    guard: 'Point, scope, handler, version and failure-policy checks pass'
    side_effects:
      - Commit a security audit entry.
    who: Extension binding administrator
    provenance: test
    source: >-
      services-platform:modules/extensions/tests/unit/lifecycle.spec.js#allows draft to become enabled, shadow
      or disabled, enabled and shadow to trade places, disabled to return, and orphaned only to be disabled
  - from: draft
    to: shadow
    trigger: Run an attachment in shadow
    guard: The same contract and binding checks pass
    side_effects:
      - Commit a security audit entry.
    who: Extension binding administrator
    provenance: test
    source: >-
      services-platform:modules/extensions/tests/unit/lifecycle.spec.js#allows draft to become enabled, shadow
      or disabled, enabled and shadow to trade places, disabled to return, and orphaned only to be disabled
  - from: draft
    to: disabled
    trigger: Set a prepared attachment aside
    guard: The update passes normal binding validation and version checks
    side_effects:
      - Commit a security audit entry.
    who: Extension binding administrator
    provenance: observed
    source: 'services-platform:modules/extensions/lifecycle.ts:BINDING_TRANSITIONS'
  - from: enabled
    to: shadow
    trigger: Switch applied work to shadow
    guard: Normal validation and the single-answer uniqueness rule permit it
    side_effects:
      - Commit a security audit entry.
    who: Extension binding administrator
    provenance: observed
    source: 'services-platform:modules/extensions/lifecycle.ts:BINDING_TRANSITIONS'
  - from: shadow
    to: enabled
    trigger: Promote shadow work
    guard: Normal contract and binding validation succeeds
    side_effects:
      - Commit a security audit entry.
    who: Extension binding administrator
    provenance: observed
    source: 'services-platform:modules/extensions/lifecycle.ts:BINDING_TRANSITIONS'
  - from: enabled
    to: disabled
    trigger: Stop applied work
    guard: The update passes normal validation and version checks
    side_effects:
      - Commit a security audit entry.
    who: Extension binding administrator
    provenance: observed
    source: 'services-platform:modules/extensions/lifecycle.ts:BINDING_TRANSITIONS'
  - from: shadow
    to: disabled
    trigger: Stop shadow work
    guard: The update passes normal validation and version checks
    side_effects:
      - Commit a security audit entry.
    who: Extension binding administrator
    provenance: observed
    source: 'services-platform:modules/extensions/lifecycle.ts:BINDING_TRANSITIONS'
  - from: disabled
    to: draft
    trigger: Prepare a stopped attachment again
    guard: The update passes normal validation and version checks
    side_effects:
      - Commit a security audit entry.
    who: Extension binding administrator
    provenance: observed
    source: 'services-platform:modules/extensions/lifecycle.ts:BINDING_TRANSITIONS'
  - from: disabled
    to: enabled
    trigger: Reapply a stopped attachment
    guard: Normal contract and binding validation succeeds
    side_effects:
      - Commit a security audit entry.
    who: Extension binding administrator
    provenance: observed
    source: 'services-platform:modules/extensions/lifecycle.ts:BINDING_TRANSITIONS'
  - from: disabled
    to: shadow
    trigger: Restart stopped work in shadow
    guard: Normal contract and binding validation succeeds
    side_effects:
      - Commit a security audit entry.
    who: Extension binding administrator
    provenance: observed
    source: 'services-platform:modules/extensions/lifecycle.ts:BINDING_TRANSITIONS'
  - from: enabled
    to: orphaned
    trigger: Withdraw the point with applied work
    guard: The supplied owner declaration omits the point
    side_effects:
      - Log orphaned-binding count as a warning.
    who: 'Registration intake, across every workspace'
    provenance: test
    source: >-
      services-platform:modules/extensions/tests/integration/services/registration/register-service-extensions.integration.spec.js#orphans
      every binding of a retired point, in every tenant, whatever its status
  - from: draft
    to: orphaned
    trigger: Withdraw the point with prepared work
    guard: The supplied owner declaration omits the point
    side_effects:
      - Log orphaned-binding count as a warning.
    who: Registration intake
    provenance: test
    source: >-
      services-platform:modules/extensions/tests/integration/services/registration/register-service-extensions.integration.spec.js#orphans
      every binding of a retired point, in every tenant, whatever its status
  - from: shadow
    to: orphaned
    trigger: Withdraw the point with shadow work
    guard: The supplied owner declaration omits the point
    side_effects:
      - Log orphaned-binding count as a warning.
    who: Registration intake
    provenance: test
    source: >-
      services-platform:modules/extensions/tests/integration/services/registration/register-service-extensions.integration.spec.js#orphans
      every binding of a retired point, in every tenant, whatever its status
  - from: disabled
    to: orphaned
    trigger: Withdraw the point with stopped work
    guard: The supplied owner declaration omits the point
    side_effects:
      - Log orphaned-binding count as a warning.
    who: Registration intake
    provenance: test
    source: >-
      services-platform:modules/extensions/tests/integration/services/registration/register-service-extensions.integration.spec.js#orphans
      every binding of a retired point, in every tenant, whatever its status
  - from: orphaned
    to: disabled
    trigger: Stop an orphaned attachment
    guard: The requested lifecycle move is disabled and remaining validation passes
    side_effects:
      - Commit a security audit entry.
    who: Extension binding administrator
    provenance: test
    source: >-
      services-platform:modules/extensions/tests/unit/lifecycle.spec.js#allows draft to become enabled, shadow
      or disabled, enabled and shadow to trade places, disabled to return, and orphaned only to be disabled
invalid:
  - from: orphaned
    to: enabled
    why: An orphaned attachment can only move to disabled; redeclaration alone leaves it orphaned.
  - from: enabled
    to: draft
    why: An applied attachment must be disabled before returning to preparation.
  - from: orphaned
    to: draft
    why: Preparation is unavailable directly from orphaned status.
concurrency: >-
  Administrative writes compare the read binding version. Single-answer scope uniqueness applies to enabled
  and shadow entries together. Effective intervals restrict resolution; preview screens do not prove service
  refusals.
invariants:
  - INV-DOM-PFM-INT-005-03
  - INV-DOM-PFM-INT-005-04
  - INV-DOM-PFM-INT-005-05
  - INV-DOM-PFM-INT-005-09
evidence:
  - >-
    services-platform at ad3f6a65: modules/extensions/lifecycle.ts; db/extension-bindings.table.ts;
    services/persistence/extension-bindings.ts; services/registration/register-service-extensions.ts;
    docs/extensions.md (Bindings)
---

# Extension binding lifecycle

## The Lifecycle

```mermaid
stateDiagram-v2
  state "draft" as s0
  state "enabled" as s1
  state "shadow" as s2
  state "disabled" as s3
  state "orphaned" as s4
  s0 --> s1: Apply a prepared attachment
  s0 --> s2: Run an attachment in shadow
  s0 --> s3: Set a prepared attachment aside
  s1 --> s2: Switch applied work to shadow
  s2 --> s1: Promote shadow work
  s1 --> s3: Stop applied work
  s2 --> s3: Stop shadow work
  s3 --> s0: Prepare a stopped attachment again
  s3 --> s1: Reapply a stopped attachment
  s3 --> s2: Restart stopped work in shadow
  s1 --> s4: Withdraw the point with applied work
  s0 --> s4: Withdraw the point with prepared work
  s2 --> s4: Withdraw the point with shadow work
  s3 --> s4: Withdraw the point with stopped work
  s4 --> s3: Stop an orphaned attachment
```

## Transitions Explained

Attachments can be prepared, applied, shadowed or disabled. Registration alone can orphan them when it withdraws a point. Orphaned records retain their customer history and have only the disabled administrative move; normal validation still applies to updates.

## What Cannot Happen

An orphaned attachment can only move to disabled; redeclaration alone leaves it orphaned. An applied attachment must be disabled before returning to preparation. Preparation is unavailable directly from orphaned status.

## Concurrency and Timing

Administrative writes compare the read binding version. Single-answer scope uniqueness applies to enabled and shadow entries together. Effective intervals restrict resolution; preview screens do not prove service refusals.
