---
id: CAP-MBR-STR-018
title: Maintain configuration with consistent change safeguards
feature: FEAT-MBR-STR-06
product_area: membership
module: structure
revision: 1
status: draft
product_intent: proposed
implementation_evidence: observed
supersedes:
  - react-console acceptance inventory CAP-MBR-CFG-013
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources: []
purpose: >-
  Administrators change membership configuration without silently overwriting newer edits or removing
  referenced records.
outcomes:
  - Configuration mutations identify the version being changed.
  - Record and owned-list changes succeed or fail together.
concepts:
  - CON-MBR-STR-006
actors:
  - ACT-MBR-001
  - ACT-MBR-002
  - ACT-MBR-003
surfaces:
  - api
  - job
preconditions: []
postconditions: []
permissions:
  - key: membership.programs.read
    where_checked: api
    scope: tenant
  - key: membership.programs.manage
    where_checked: api
    scope: tenant
  - key: membership.configuration.read
    where_checked: api
    scope: tenant
  - key: membership.configuration.manage
    where_checked: api
    scope: tenant
  - key: membership.application.access
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-MBR-STR-018-01
    proved_by: [ATS-MBR-STR-018-01, TST-MBR-STR-021, TST-MBR-STR-022]
    statement: >-
      Programs, types, reasons and policy sets use supplied or name-derived readable identifiers of at most 63
      characters; packages use the product identity instead.
    kind: validation
    enforced_at:
      - api
      - service
      - database
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/configuration/services/configuration/configuration-records.ts:createConfiguration
    origin: none
  - id: BR-CAP-MBR-STR-018-02
    proved_by: [ATS-MBR-STR-018-02, TST-MBR-STR-022]
    statement: A retained record’s identifier is immutable and duplicate creation fails without changing it.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/configuration/services/configuration/configuration-records.ts:updateConfiguration
    origin: none
  - id: BR-CAP-MBR-STR-018-03
    proved_by: [ATS-MBR-STR-018-03, ATS-MBR-STR-018-13, TST-MBR-STR-023, TST-MBR-STR-024]
    statement: >-
      Changes and deletions require a positive version and reject stale versions without applying the
      mutation.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: observed
    source: 'services-membership:modules/configuration/services/persistence/configuration-store.ts:configurationStore'
    origin: none
  - id: BR-CAP-MBR-STR-018-04
    proved_by: [ATS-MBR-STR-018-04, TST-MBR-STR-025]
    statement: >-
      Configuration updates keep omitted keys, clear nullable keys when null is supplied, normalize empty
      strings, reject blank required text and unknown keys, and replace only submitted lists.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/configuration/services/configuration/configuration-records.ts:updateConfiguration
    origin: none
  - id: BR-CAP-MBR-STR-018-05
    proved_by: [ATS-MBR-STR-018-05, ATS-MBR-STR-018-14, TST-MBR-STR-026, TST-MBR-STR-055]
    statement: >-
      Foreign-key dependencies prevent deletion, while removal of an unreferenced record includes its owned
      lists.
    kind: lifecycle
    enforced_at:
      - database
      - service
    must_prove: true
    provenance: observed
    source: 'services-membership:modules/configuration/services/persistence/configuration-store.ts:configurationStore'
    origin: none
  - id: BR-CAP-MBR-STR-018-06
    proved_by: [ATS-MBR-STR-018-06, ATS-MBR-STR-018-10, ATS-MBR-STR-018-11, ATS-MBR-STR-018-12, TST-MBR-STR-053, TST-MBR-STR-092]
    statement: >-
      A configuration record and its submitted lists are written transactionally, leaving no partial create or
      update when validation fails.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/configuration/services/configuration/configuration-records.ts:createConfiguration
    origin: none
  - id: BR-CAP-MBR-STR-018-07
    proved_by: [ATS-MBR-STR-018-07, TST-MBR-STR-095]
    statement: >-
      Known database checks and reference failures become field-naming refusals, with out-of-range values
      rejected by bounded input schemas or translated database errors.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    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 a type of a program that does not exist 400 naming program_id
    origin: none
  - id: BR-CAP-MBR-STR-018-08
    proved_by: [ATS-MBR-STR-018-09]
    statement: >-
      Configuration lists default to 50 records in identifier order and cap oversized requests at 500;
      activity-filtered package pages can remain continuable even when empty.
    kind: calculation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/configuration/services/persistence/membership-packages.ts:listMembershipPackages
    origin: none
  - id: BR-CAP-MBR-STR-018-09
    proved_by: [ATS-MBR-STR-018-08, ATS-MBR-STR-019-08, TST-MBR-STR-029]
    statement: 'Another workspace’s record behaves as missing for reads, updates and deletions.'
    kind: permission
    enforced_at:
      - database
      - service
    must_prove: true
    provenance: observed
    source: 'services-membership:aws/api/route-inputs.ts:found'
    origin: none
  - id: BR-CAP-MBR-STR-018-10
    proved_by: []
    statement: These configuration operations neither expose nor honor the stored lock flag.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: observed
    source: 'services-membership:modules/configuration/services/persistence/configuration-store.ts:configurationStore'
    origin: none
  - id: BR-CAP-MBR-STR-018-11
    proved_by: []
    statement: An unchanged configuration save advances the version without creating a content revision.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: documented
    source: 'react-console:acceptance/membership/journeys/configuration/MBR-ACC-0122.json'
    origin: none
scenarios:
  - id: SC-CAP-MBR-STR-018-01
    given: Two staff editors read the same package version.
    when: Each submits a change using that original version.
    then: The first change saves and the second must reconcile the newer record.
    provenance: test
    source: >-
      services-membership:tests/acceptance/configuration/MBR-ACC-0084.acceptance.ts#MBR-ACC-0084: Two
      administrators edit the Regular package at the same time: the first save wins, the second is told to
      re-read, and the grants she re-sends get new ids
edge_cases:
  - id: SC-CAP-MBR-STR-018-02
    given: An integration requests 501 configuration records.
    when: The configuration list handles the request.
    then: The response is limited to 500 without an oversized-request refusal.
    provenance: documented
    source: 'react-console:acceptance/membership/journeys/configuration/MBR-ACC-0087.json'
constraints: []
depends_on: []
related:
  - CAP-MBR-STR-009
  - CAP-MBR-STR-011
  - CAP-MBR-STR-012
  - CAP-MBR-STR-014
  - CAP-MBR-STR-017
journeys:
  - JNY-MBR-STR-004
open_questions: []
evidence:
  - >-
    services-membership: Examination of the configuration route builder, record processes, the shared store,
    record helpers and route inputs.
  - >-
    services-membership: Examination of database refusals end-to-end and acceptance journeys MBR-ACC-0081 to
    0089, 0116, 0118, 0122.
  - 'react-console: Review of acceptance inventory CAP-MBR-CFG-013.'
  - 'react-console:acceptance/membership/journeys/configuration/MBR-ACC-0122.json'
  - 'services-membership:aws/api/route-inputs.ts'
  - 'services-membership:modules/configuration/services/configuration/configuration-records.ts'
  - 'services-membership:modules/configuration/services/persistence/configuration-store.ts'
  - 'services-membership:tests/acceptance/configuration/MBR-ACC-0117.acceptance.ts'
  - 'services-membership:tests/e2e/database-refusals.e2e.spec.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-003, TC-PI-005, TC-SEC-001, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003]
exercised_by: [FAS-MBR-STR-04-03, FAS-MBR-STR-04-05, FAS-MBR-STR-06-01, FAS-MBR-STR-06-02, FAS-MBR-STR-06-03, FAS-MBR-STR-06-04, FAS-MBR-STR-06-05]
suites: [TST-MBR-STR-021, TST-MBR-STR-022, TST-MBR-STR-023, TST-MBR-STR-024, TST-MBR-STR-025, TST-MBR-STR-026, TST-MBR-STR-029, TST-MBR-STR-053, TST-MBR-STR-055, TST-MBR-STR-092, TST-MBR-STR-095]
---

# Maintain configuration with consistent change safeguards

## Purpose

Administrators change membership configuration without silently overwriting newer edits or removing referenced records.

## How It Works

An administrator reads a configuration record before submitting a change. The request identifies that version. The service rejects an intervening edit rather than silently replacing it. Configuration changes are partial, retaining omitted keys. Submitted child lists use complete-list replacement. Validation evaluates the record and children within one transaction. A failing list therefore leaves the earlier aggregate intact. References can also prevent deletion, with staff directed toward retirement where available. Lists provide bounded continuation in identifier order. These safeguards differ from the structure’s full replacements and some optional version checks. The product should make those differences clear and decide whether stored locks become operational controls.

## Relationships

- Programs, types, packages, reasons and policy sets use these shared safeguards.
- Access controls decide which administrator can invoke them.
- Bulk configuration operations reuse the ordinary configuration processes.
