---
id: WF-MBR-STR-005
title: Save a configuration record with its owned lists
trigger: >-
  An administrator or bulk item creates or updates configuration, deletes an unreferenced record, or replaces
  a program's complete requirement list.
product_area: membership
module: structure
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
inference: []
implements:
  - CAP-MBR-STR-018
  - CAP-MBR-STR-009
  - CAP-MBR-STR-010
  - CAP-MBR-STR-011
  - CAP-MBR-STR-014
  - CAP-MBR-STR-017
moves:
  - STATE-MBR-STR-006
crosses:
  - SVC-MBR-STR-configuration
  - SVC-MBR-STR-structure
idempotency: >-
  Duplicate creation is rejected. Updates and deletions require the read version, so replaying a successful
  update is stale. An unchanged save still advances the version but adds no content revision.
concurrency: >-
  Conditional record writes reject competing stale saves. Requirement replacement checks live affiliations
  without locking them; a concurrent affiliation addition can escape the pre-write in-use check.
hops:
  - 'n': 1
    actor_or_service: Configuration API
    action: >-
      Authorize program or configuration management and strictly parse applicable content; updates and
      deletion require positive versions.
    anchor: 'services-membership:aws/api/configuration-routes.ts:configurationRoutes'
    seam: sync
    on_failure: Return authorization or field validation errors before mutation.
    left_inconsistent: No partial Membership write survives the failed transaction.
    observer: the caller
    provenance: observed
    source: 'services-membership:modules/configuration/services/shared/records.ts:parseBody'
  - 'n': 2
    actor_or_service: Configuration service
    action: >-
      Resolve create identity or load the current record for an update; preserve omitted content and clear
      nullable fields only when explicitly requested.
    anchor: >-
      services-membership:modules/configuration/services/configuration/configuration-records.ts:updateConfiguration
    seam: sync
    on_failure: Reject a missing update target or invalid identity.
    left_inconsistent: No partial Membership write survives the failed transaction.
    observer: the caller
    provenance: observed
    source: >-
      services-membership:modules/configuration/services/configuration/configuration-records.ts:updateConfiguration
  - 'n': 3
    actor_or_service: Configuration service
    action: >-
      Validate referenced structure, requirement uniqueness and counts, current affiliation use of dropped
      kinds, and package rule combinations.
    anchor: >-
      services-membership:modules/configuration/services/configuration/configuration-records.ts:replaceProgramUnitTypesOf
    seam: sync
    on_failure: Reject the collected configuration problems or the in-use conflict.
    left_inconsistent: No partial Membership write survives the failed transaction.
    observer: the caller
    provenance: observed
    source: >-
      services-membership:modules/configuration/services/configuration/configuration-records.ts:unitTypeProblems
  - 'n': 4
    actor_or_service: Configuration service
    action: >-
      Write the record at its required version and replace only submitted lists. Grants and policies receive
      new generated identities; other lists retain their composite key schemes.
    anchor: 'services-membership:modules/configuration/services/persistence/configuration-store.ts:configurationStore'
    seam: sync
    on_failure: A stale version or child write failure rolls back the save.
    left_inconsistent: No partial Membership write survives the failed transaction.
    observer: the caller
    provenance: observed
    source: >-
      services-membership:modules/configuration/services/configuration/configuration-records.ts:createConfiguration
  - 'n': 5
    actor_or_service: Membership database
    action: >-
      Apply declared database reference and value constraints; translate known create and update refusals
      after rollback to the relevant fields.
    anchor: 'services-membership:modules/configuration/services/shared/records.ts:translateDatabaseRefusal'
    seam: sync
    on_failure: 'Reject the complete transaction, including every submitted list.'
    left_inconsistent: No partial Membership write survives the failed transaction.
    observer: the caller
    provenance: test
    source: >-
      services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches, through
      the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column refuses ›
      refuses flow-down enabled without a target type 400 naming flowdown_type_id, and writes no type
  - 'n': 6
    actor_or_service: Membership history capture
    action: >-
      Settle content revisions in the writing transaction at commit; later dispatcher work drains history-only
      captured events.
    anchor: 'services-membership:aws/membership.change-events.ts:membershipChangeEventStreams'
    seam: sync
    on_failure: >-
      Transactional history settlement failure prevents commit; draining afterward does not create the
      revision.
    left_inconsistent: No delayed revision is caused by dispatcher lag; unchanged content receives no new content revision.
    observer: record revisions; change-events dead-letter queue
    provenance: observed
    source: 'services-membership:aws/membership.change-events.ts:HISTORY_CHANGE_EVENT_STREAMS'
branches:
  - >-
    Deletion uses the store's versioned removal and foreign-key protection rather than content and child-save
    steps.
  - Requirement replacement advances the program version and rewrites the full requirement list.
waits: []
evidence:
  - >-
    services-membership at 5994c270: aws/api/configuration-routes.ts;
    modules/configuration/services/configuration/configuration-records.ts (createConfiguration,
    updateConfiguration, replaceProgramUnitTypesOf, unitTypeProblems);
    services/persistence/configuration-store.ts; services/shared/records.ts; aws/membership.change-events.ts.
controls: [TC-AVL-002, TC-AVL-003, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-CHG-001, TC-CHG-002, TC-CHG-003, TC-CHG-004, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-DAT-001, TC-DAT-004, TC-DAT-007, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-005, TC-SEC-001, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003]
---

# Save a configuration record with its owned lists

## What It Does

Configuration saves share strict parsing, reference validation, versioned mutation and list handling. Omitted lists remain, submitted empty lists clear rows, and the complete result commits with content history. The save does not turn imports into a different configuration contract.

## The Flow

```mermaid
flowchart TD
  A[Configuration request] --> B[Authority and strict content validation]
  B --> C[Resolve identity or current record]
  C --> D[Check references and combined rules]
  D --> E[Write record and submitted lists at version]
  E --> F[Database checks and transactional history settlement]
  F --> G[Commit and return record]
  B -->|invalid| X[Refuse without partial result]
  D -->|invalid| X
  E -->|stale or failed| X
  F -->|failed| X
```

## When Something Fails

Pre-commit failure leaves neither a partial record nor a partly replaced list. Error translation occurs after failed create or update transactions unwind. Requirement replacement retains a concurrency gap because its affiliation-use check does not lock the memberships that can add new affiliations.

## Open Design Questions

Product must settle replacement consistency and the unused lock flag. Individual grants and policies receive new identities when replaced, so consumers cannot assume those identities survive a submitted-list save.
