---
id: ATS-CRM-ENT-018
scope: CAP-CRM-ENT-018
product_area: crm
module: entities
revision: 1
status: proposed
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-CRM-ENT-018-01
    title: Names required and trimmed
    level: api
    proves:
      - BR-CAP-CRM-ENT-018-01
    preconditions: No additional setup is required.
    steps: 'Create each type of type with an empty name, spaces and a padded name.'
    expected: '400 naming the field, no record changes; trimmed.'
    negative: true
    data_needs: No additional fixtures.
    bound_to:
      - TST-CRM-ENT-786
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
      400 naming the field for a create whose name is nothing but spaces
    category: validation
    action_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        400 naming the field for a create whose name is nothing but spaces
    assertion_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        400 naming the field for a create whose name is nothing but spaces
    test_anchors:
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        400 naming the field for a create whose name is the empty string
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        400 naming the field for a create whose name is nothing but spaces
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#writes
        no type at all for a create it refused: the id reads back 404
      - >-
        services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#answers
        400 naming the field for a create whose name is nothing but spaces
      - >-
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#answers
        400 without a name
      - >-
        services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
        400 without a name
    binding_scope: >-
      contributions: the cited executable assertions collectively support only
      the observations they contain; additional matrix and journey checks remain
      proposed.
  - id: ATS-CRM-ENT-018-02
    title: 'Ids derived, valid, unreserved, unique, fixed'
    level: api
    proves:
      - BR-CAP-CRM-ENT-018-02
    preconditions: No additional setup is required.
    steps: >-
      Create without an id; with a bad id; a reserved id; a used id; replace
      with another id than the path.
    expected: Derived; 400 each; 400.
    negative: true
    data_needs: No additional fixtures.
    bound_to:
      - TST-CRM-ENT-786
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
      400 for an id that is not a configuration id, saying what an id may hold
    category: boundary
    action_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        400 for an id that is not a configuration id, saying what an id may hold
    assertion_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        400 for an id that is not a configuration id, saying what an id may hold
    test_anchors:
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#derives
        the id from the name when the create names none
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        400 for an id that is not a configuration id, saying what an id may hold
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        400 for a reserved id
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        400 for an id the tenant already uses
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
        when the id is wrong, refuses the change (400)
      - >-
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#answers
        400 for an id the API keeps for itself
    binding_scope: >-
      contributions: the cited executable assertions collectively support only
      the observations they contain; additional matrix and journey checks remain
      proposed.
  - id: ATS-CRM-ENT-018-03
    title: Types in use cannot be deleted
    level: api
    proves:
      - BR-CAP-CRM-ENT-018-03
    preconditions: >-
      Prepare a contact type, an organization type, an address type and a phone
      type each in use.
    steps: Delete each.
    expected: '409 each, naming the records that keep it for address and phone types.'
    negative: true
    data_needs: No additional fixtures.
    bound_to:
      - TST-CRM-ENT-786
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#says
      which records keep the type when it refuses the delete
    category: interaction
    action_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#says
        which records keep the type when it refuses the delete
    assertion_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#says
        which records keep the type when it refuses the delete
    test_anchors:
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#refuses
        the delete of a type a contact has (409)
      - >-
        services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#refuses
        an organization of a type the tenant does not have (409), and the delete
        of a type an organization has (409)
      - >-
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#refuses
        the delete of a type an address has (409)
      - >-
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#says
        which records keep the type when it refuses the delete
      - >-
        services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#refuses
        the delete of a type a number has (409)
    binding_scope: >-
      contributions: the cited executable assertions collectively support only
      the observations they contain; additional matrix and journey checks remain
      proposed.
  - id: ATS-CRM-ENT-018-04
    title: 'Reorders are complete, rejected cleanly, unblocked by locks'
    level: api
    proves:
      - BR-CAP-CRM-ENT-018-04
    preconditions: 'Prepare each list with several types, one locked.'
    steps: >-
      Reorder all; leave one out; name one twice; name an unknown one; create a
      new address type.
    expected: >-
      1..n persisted with versions advanced; 400 each with the order unmodified;
      the new type at the end.
    negative: true
    data_needs: No additional fixtures.
    bound_to:
      - TST-CRM-ENT-786
      - TST-CRM-ENT-787
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
      400 for a reorder that leaves a type of the tenant out
    category: invariant
    action_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        400 for a reorder that leaves a type of the tenant out
    assertion_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        400 for a reorder that leaves a type of the tenant out
    test_anchors:
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        the types in the order the body listed them, numbered 1..n
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        400 for a reorder that leaves a type of the tenant out
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        400 for a reorder that names a type twice
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#leaves
        the order as the successful reorder left it after every refusal
      - >-
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#puts a
        new type at the end of the tenant’s order when the body names no place
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#lists
        the types of a tenant that has arranged nothing alphabetically, each
        holding no place
      - >-
        services-crm:modules/entities/tests/integration/services/persistence/contact-types.integration.spec.js#numbers
        a placeless type locked against updates that the first reorder leaves
        where its name stands
    binding_scope: >-
      contributions: the cited executable assertions collectively support only
      the observations they contain; additional matrix and journey checks remain
      proposed.
  - id: ATS-CRM-ENT-018-05
    title: Address and phone type applicability
    level: api
    proves:
      - BR-CAP-CRM-ENT-018-05
    preconditions: 'Prepare types for contacts, organizations and both.'
    steps: >-
      Create without applicability; list by type; mark a used type for
      organizations.
    expected: >-
      Contacts; each type's list with both-types in each; accepted and the
      contact still returned.
    negative: false
    data_needs: No additional fixtures.
    bound_to:
      - TST-CRM-ENT-786
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
      only the types usable on organizations when the query asks for them
    category: rule
    action_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
        only the types usable on organizations when the query asks for them
    assertion_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
        only the types usable on organizations when the query asks for them
    test_anchors:
      - >-
        services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#applies
        a type to contacts when the create says nothing
      - >-
        services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
        only the types usable on organizations when the query asks for them
      - >-
        services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
        a type of both in the lists of each kind
      - >-
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#takes
        the type being marked for organizations while a contact address has it
        (200)
      - >-
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#still
        answers the contact its address of that type
    binding_scope: >-
      contributions: the cited executable assertions collectively support only
      the observations they contain; additional matrix and journey checks remain
      proposed.
  - id: ATS-CRM-ENT-018-06
    title: Who may change types
    level: api
    proves:
      - BR-CAP-CRM-ENT-018-06
    preconditions: Prepare read-only callers.
    steps: Write and reorder each type of type.
    expected: 403 for writes and reorders; retrieves allowed.
    negative: true
    data_needs: Use personas by key.
    bound_to:
      - TST-CRM-ENT-786
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#answers
      403 to a read-only caller’s reorder
    category: permission
    action_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#answers
        403 to a read-only caller’s reorder
    assertion_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#answers
        403 to a read-only caller’s reorder
    test_anchors:
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
        403 to a caller who may only read contact types
      - >-
        services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#answers
        403 to a caller who may only read organization types, and lets that
        caller read
      - >-
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#answers
        403 to a read-only caller’s reorder
      - >-
        services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
        403 to a read-only caller’s reorder
      - >-
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#lets a
        caller who may only read address types read them
    binding_scope: >-
      contributions: the cited executable assertions collectively support only
      the observations they contain; additional matrix and journey checks remain
      proposed.
  - id: ATS-CRM-ENT-018-07
    title: 'Version, patch and lock on types'
    level: api
    proves:
      - BR-CAP-CRM-ENT-018-07
    preconditions: 'Prepare a type, one locked against updates, one against deletes.'
    steps: >-
      Replace at a stale version; patch one field; change the update-locked one;
      delete the delete-locked one.
    expected: 409; only that field changed; rejected; rejected.
    negative: true
    data_needs: No additional fixtures.
    bound_to:
      - TST-CRM-ENT-786
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
      when the version is off, refuses the change (409)
    category: concurrency
    action_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
        when the version is off, refuses the change (409)
    assertion_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
        when the version is off, refuses the change (409)
    test_anchors:
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
        when the version is off, refuses the change (409)
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#and
        leaves what the patch said nothing about as it was
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#refuses
        changing a type locked against updates
      - >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#refuses
        deleting a type locked against deletes
      - >-
        services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#refuses
        changing a type locked against updates, and deleting one locked against
        deletes
    binding_scope: >-
      contributions: the cited executable assertions collectively support only
      the observations they contain; additional matrix and journey checks remain
      proposed.
  - id: ATS-CRM-ENT-018-08
    title: A held inactive classification remains readable
    category: edge
    level: api
    proves:
      - BR-CAP-CRM-ENT-018-08
    preconditions: A person holds a classification that was active when selected.
    steps: >-
      Deactivate that classification; open the person and the new-person type
      chooser.
    expected: >-
      The held classification remains visible on the person; the chooser
      excludes it from new selection.
    negative: false
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to:
      - TST-CRM-ENT-774
    provenance: test
    source: >-
      react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0033.spec.ts#CRM-ACC-0033
    test_anchors:
      - >-
        react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0033.spec.ts#CRM-ACC-0033
    action_evidence:
      provenance: test
      source: >-
        react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0033.spec.ts#CRM-ACC-0033
    assertion_evidence:
      provenance: test
      source: >-
        react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0033.spec.ts#CRM-ACC-0033
    binding_scope: >-
      contributions: the cited executable assertions collectively support only
      the observations they contain; additional matrix and journey checks remain
      proposed.
  - id: ATS-CRM-ENT-018-09
    title: Keep the classification chosen before deactivation
    category: interaction
    level: api
    proves:
      - BR-CAP-CRM-ENT-001-09
      - BR-CAP-CRM-ENT-018-08
    preconditions: A coordinator has selected an active person classification.
    steps: >-
      Deactivate the classification before creation completes; save the person
      and reopen the chooser.
    expected: >-
      The saved person retains that classification while a new selection
      excludes the inactive choice.
    negative: false
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers 409
      for a type the tenant does not have, and saves no contact;
      react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0033.spec.ts#CRM-ACC-0033
    exercises:
      - CAP-CRM-ENT-001
      - CAP-CRM-ENT-018
    workflows: []
    test_anchors: []
    action_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers 409
        for a type the tenant does not have, and saves no contact;
        react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0033.spec.ts#CRM-ACC-0033
    assertion_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers 409
        for a type the tenant does not have, and saves no contact;
        react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0033.spec.ts#CRM-ACC-0033
    binding_scope: 'unbound: the detailed scenario still needs executable proof.'
  - id: ATS-CRM-ENT-018-10
    title: Prevent deletion of a classification held by a body
    category: interaction
    level: api
    proves:
      - BR-CAP-CRM-ENT-002-04
      - BR-CAP-CRM-ENT-018-03
    preconditions: An organization uses a workspace classification.
    steps: >-
      Try to delete the classification, then read the organization and
      classification.
    expected: >-
      The referenced classification remains and the organization keeps its
      classification identity.
    negative: false
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#refuses
      an organization of a type the tenant does not have (409), and the delete
      of a type an organization has (409);
      services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#says
      which records keep the type when it refuses the delete
    exercises:
      - CAP-CRM-ENT-002
      - CAP-CRM-ENT-018
    workflows: []
    test_anchors: []
    action_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#refuses
        an organization of a type the tenant does not have (409), and the delete
        of a type an organization has (409);
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#says
        which records keep the type when it refuses the delete
    assertion_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#refuses
        an organization of a type the tenant does not have (409), and the delete
        of a type an organization has (409);
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#says
        which records keep the type when it refuses the delete
    binding_scope: 'unbound: the detailed scenario still needs executable proof.'
  - id: ATS-CRM-ENT-018-11
    title: Retain a held purpose after its applicability changes
    category: interaction
    level: api
    proves:
      - BR-CAP-CRM-ENT-005-07
      - BR-CAP-CRM-ENT-018-05
    preconditions: A person holds an address purpose that currently applies to people.
    steps: >-
      Change the purpose to organizations only; save the person with its
      existing address; try assigning that purpose to another person.
    expected: The held address remains saveable; the new person assignment is refused.
    negative: false
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contacts.field-rules.integration.spec.js#leaves
      a contact saveable after the type of an address it holds became
      organizations-only;
      services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
      only the types usable on organizations when the query asks for them
    exercises:
      - CAP-CRM-ENT-005
      - CAP-CRM-ENT-018
    workflows: []
    test_anchors: []
    action_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/integration/services/persistence/contacts.field-rules.integration.spec.js#leaves
        a contact saveable after the type of an address it holds became
        organizations-only;
        services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
        only the types usable on organizations when the query asks for them
    assertion_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/integration/services/persistence/contacts.field-rules.integration.spec.js#leaves
        a contact saveable after the type of an address it holds became
        organizations-only;
        services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
        only the types usable on organizations when the query asks for them
    binding_scope: 'unbound: the detailed scenario still needs executable proof.'
  - id: ATS-CRM-ENT-018-12
    title: Policy editing takes effect when reprocessing reaches the body
    category: interaction
    level: api
    proves:
      - BR-CAP-CRM-ENT-012-06
      - BR-CAP-CRM-ENT-018-07
    preconditions: >-
      An existing organization is compliant under its classification’s old
      policy.
    steps: >-
      Increase the minimum in the classification; read the body, request
      reprocessing and read it after settlement.
    expected: >-
      The first read retains stored coverage; settled reprocessing records the
      new deficient coverage.
    negative: false
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/entities/tests/e2e/organization-compliance.e2e.spec.js#keep
      them against a PUT that sends others back;
      services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
      when the version is off, refuses the change (409)
    exercises:
      - CAP-CRM-ENT-012
      - CAP-CRM-ENT-018
    workflows:
      - WF-CRM-ENT-008
    test_anchors: []
    action_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/e2e/organization-compliance.e2e.spec.js#keep
        them against a PUT that sends others back;
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
        when the version is off, refuses the change (409)
    assertion_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/e2e/organization-compliance.e2e.spec.js#keep
        them against a PUT that sends others back;
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
        when the version is off, refuses the change (409)
    binding_scope: 'unbound: the detailed scenario still needs executable proof.'
  - id: ATS-CRM-ENT-018-13
    title: A locked classification blocks its ordinary mutation
    category: interaction
    level: api
    proves:
      - BR-CAP-CRM-ENT-015-09
      - BR-CAP-CRM-ENT-018-07
    preconditions: A classification is already locked against updates.
    steps: >-
      Attempt a classification correction and deletion through its ordinary
      routes.
    expected: >-
      Both operations are refused and the classification remains; this catalogue
      has no dedicated release action.
    negative: false
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:docs/record-locks.md#Not covered yet;
      services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
      when the version is off, refuses the change (409)
    exercises:
      - CAP-CRM-ENT-015
      - CAP-CRM-ENT-018
    workflows: []
    test_anchors: []
    action_evidence:
      provenance: proposed
      source: >-
        services-crm:docs/record-locks.md#Not covered yet;
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
        when the version is off, refuses the change (409)
    assertion_evidence:
      provenance: proposed
      source: >-
        services-crm:docs/record-locks.md#Not covered yet;
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
        when the version is off, refuses the change (409)
    binding_scope: 'unbound: the detailed scenario still needs executable proof.'
  - id: ATS-CRM-ENT-018-14
    title: Default mappings retain their contact-point purposes
    category: interaction
    level: api
    proves:
      - BR-CAP-CRM-ENT-017-07
      - BR-CAP-CRM-ENT-018-03
    preconditions: >-
      An affiliation type references address and phone purposes in its default
      mappings.
    steps: >-
      Attempt deletion of a referenced purpose; remove the mapping and retry
      when no record holds the purpose.
    expected: >-
      The reference blocks the first delete; the unused purpose is deletable
      after the mapping is removed.
    negative: false
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/affiliation-types.integration.spec.js#keeps
      the mappings, as an omitted collection means unchanged;
      services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#says
      which records keep the type when it refuses the delete
    exercises:
      - CAP-CRM-ENT-017
      - CAP-CRM-ENT-018
    workflows: []
    test_anchors: []
    action_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/integration/services/persistence/affiliation-types.integration.spec.js#keeps
        the mappings, as an omitted collection means unchanged;
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#says
        which records keep the type when it refuses the delete
    assertion_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/integration/services/persistence/affiliation-types.integration.spec.js#keeps
        the mappings, as an omitted collection means unchanged;
        services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#says
        which records keep the type when it refuses the delete
    binding_scope: 'unbound: the detailed scenario still needs executable proof.'
  - id: ATS-CRM-ENT-018-15
    title: Classification edits retain their presentation assignments
    category: interaction
    level: api
    proves:
      - BR-CAP-CRM-ENT-018-07
      - BR-CAP-CRM-ENT-019-05
    preconditions: A contact classification has all five form and layout slots assigned.
    steps: >-
      Replace its ordinary classification fields without the slots; then clear
      one slot with a patch and resolve definitions.
    expected: >-
      Replacement retains every assignment; clearing one slot changes only that
      slot’s effective inheritance.
    negative: false
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
      when the version is off, refuses the change (409);
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#KEEPS all
      five through a PUT that does not mention them: an older client cannot wipe
      a tenant’s assignments
    exercises:
      - CAP-CRM-ENT-018
      - CAP-CRM-ENT-019
    workflows: []
    test_anchors: []
    action_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
        when the version is off, refuses the change (409);
        services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#KEEPS
        all five through a PUT that does not mention them: an older client
        cannot wipe a tenant’s assignments
    assertion_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then,
        when the version is off, refuses the change (409);
        services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#KEEPS
        all five through a PUT that does not mention them: an older client
        cannot wipe a tenant’s assignments
    binding_scope: 'unbound: the detailed scenario still needs executable proof.'
  - id: ATS-CRM-ENT-018-16
    title: Exercise every declared authority independently
    category: permission
    level: api
    proves:
      - BR-CAP-CRM-ENT-018-06
    preconditions: >-
      An eligible record and configuration fixture exist for each operation; the
      caller can reach only the run’s workspace.
    steps: >-
      For each declared permission, execute its matching operation with that
      permission, then without it while retaining the other required grants.
      Read affected records after every refused mutation.
    expected: >-
      The authorized case reaches the operation; the missing-grant case is
      refused and preserves saved data. Record access alone does not authorize
      the other declared actions.
    negative: true
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:aws/crm.service.ts:ApplicationPermissions;
      services-crm:modules/entities/aws/api/affiliation-types.routes.ts;
      services-crm:modules/entities/aws/api/contact-roles.routes.ts
    permission_keys:
      - crm.contact-types.read
      - crm.contact-types.create
      - crm.contact-types.update
      - crm.contact-types.delete
      - crm.organization-types.read
      - crm.organization-types.create
      - crm.organization-types.update
      - crm.organization-types.delete
      - crm.address-types.read
      - crm.address-types.create
      - crm.address-types.update
      - crm.address-types.delete
      - crm.phone-number-types.read
      - crm.phone-number-types.create
      - crm.phone-number-types.update
      - crm.phone-number-types.delete
    test_anchors: []
    action_evidence:
      provenance: proposed
      source: >-
        services-crm:aws/crm.service.ts:ApplicationPermissions;
        services-crm:modules/entities/aws/api/affiliation-types.routes.ts;
        services-crm:modules/entities/aws/api/contact-roles.routes.ts
    assertion_evidence:
      provenance: proposed
      source: >-
        services-crm:aws/crm.service.ts:ApplicationPermissions;
        services-crm:modules/entities/aws/api/affiliation-types.routes.ts;
        services-crm:modules/entities/aws/api/contact-roles.routes.ts
    binding_scope: 'unbound: the detailed scenario still needs executable proof.'
evidence:
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
    400 naming the field for a create whose name is nothing but spaces
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
    400 naming the field for a create whose name is the empty string
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#writes no
    type at all for a create it refused: the id reads back 404
  - >-
    services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#answers
    400 naming the field for a create whose name is nothing but spaces
  - >-
    services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#answers
    400 without a name
  - >-
    services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
    400 without a name
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
    400 for an id that is not a configuration id, saying what an id may hold
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#derives
    the id from the name when the create names none
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
    400 for a reserved id
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
    400 for an id the tenant already uses
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then, when
    the id is wrong, refuses the change (400)
  - >-
    services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#answers
    400 for an id the API keeps for itself
  - >-
    services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#says which
    records keep the type when it refuses the delete
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#refuses
    the delete of a type a contact has (409)
  - >-
    services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#refuses
    an organization of a type the tenant does not have (409), and the delete of
    a type an organization has (409)
  - >-
    services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#refuses
    the delete of a type an address has (409)
  - >-
    services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#refuses
    the delete of a type a number has (409)
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
    400 for a reorder that leaves a type of the tenant out
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
    the types in the order the body listed them, numbered 1..n
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
    400 for a reorder that names a type twice
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#leaves the
    order as the successful reorder left it after every refusal
  - >-
    services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#puts a new
    type at the end of the tenant’s order when the body names no place
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#lists the
    types of a tenant that has arranged nothing alphabetically, each holding no
    place
  - >-
    services-crm:modules/entities/tests/integration/services/persistence/contact-types.integration.spec.js#numbers
    a placeless type locked against updates that the first reorder leaves where
    its name stands
  - >-
    services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
    only the types usable on organizations when the query asks for them
  - >-
    services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#applies
    a type to contacts when the create says nothing
  - >-
    services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
    a type of both in the lists of each kind
  - >-
    services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#takes the
    type being marked for organizations while a contact address has it (200)
  - >-
    services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#still
    answers the contact its address of that type
  - >-
    services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#answers
    403 to a read-only caller’s reorder
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#answers
    403 to a caller who may only read contact types
  - >-
    services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#answers
    403 to a caller who may only read organization types, and lets that caller
    read
  - >-
    services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
    403 to a read-only caller’s reorder
  - >-
    services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#lets a
    caller who may only read address types read them
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then, when
    the version is off, refuses the change (409)
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#and leaves
    what the patch said nothing about as it was
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#refuses
    changing a type locked against updates
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#refuses
    deleting a type locked against deletes
  - >-
    services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#refuses
    changing a type locked against updates, and deleting one locked against
    deletes
  - >-
    react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0033.spec.ts#CRM-ACC-0033
  - >-
    services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers 409 for
    a type the tenant does not have, and saves no contact;
    react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0033.spec.ts#CRM-ACC-0033
  - >-
    services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#refuses
    an organization of a type the tenant does not have (409), and the delete of
    a type an organization has (409);
    services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#says which
    records keep the type when it refuses the delete
  - >-
    services-crm:modules/entities/tests/integration/services/persistence/contacts.field-rules.integration.spec.js#leaves
    a contact saveable after the type of an address it holds became
    organizations-only;
    services-crm:modules/entities/tests/e2e/phone-number-types.e2e.spec.js#answers
    only the types usable on organizations when the query asks for them
  - >-
    services-crm:modules/entities/tests/e2e/organization-compliance.e2e.spec.js#keep
    them against a PUT that sends others back;
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then, when
    the version is off, refuses the change (409)
  - >-
    services-crm:docs/record-locks.md#Not covered yet;
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then, when
    the version is off, refuses the change (409)
  - >-
    services-crm:modules/entities/tests/integration/services/persistence/affiliation-types.integration.spec.js#keeps
    the mappings, as an omitted collection means unchanged;
    services-crm:modules/entities/tests/e2e/address-types.e2e.spec.js#says which
    records keep the type when it refuses the delete
  - >-
    services-crm:modules/entities/tests/e2e/contact-types.e2e.spec.js#then, when
    the version is off, refuses the change (409);
    services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#KEEPS all
    five through a PUT that does not mention them: an older client cannot wipe a
    tenant’s assignments
  - >-
    services-crm:aws/crm.service.ts:ApplicationPermissions;
    services-crm:modules/entities/aws/api/affiliation-types.routes.ts;
    services-crm:modules/entities/aws/api/contact-roles.routes.ts
coverage:
  rule: covered
  transition: 'gap: FND-CRM-ENT-066'
  validation: covered
  permission: covered
  edge: covered
  failure: 'gap: FND-CRM-ENT-066'
  boundary: covered
  concurrency: covered
  invariant: covered
  interaction: covered
previous_approval:
  revision: 1
  approved_by: Andrew
  approved_at: '2026-10-22T00:00:00.000Z'
coverage_notes:
  transition:
    reason: >-
      No detailed transition scenario currently specifies this aspect; the
      coverage finding records the required follow-through.
    provenance: unknown
    source: FND-CRM-ENT-066
  failure:
    reason: >-
      No detailed failure scenario currently specifies this aspect; the coverage
      finding records the required follow-through.
    provenance: unknown
    source: FND-CRM-ENT-066
coverage_findings:
  - FND-CRM-ENT-066
  - FND-CRM-ENT-075
permission_coverage:
  - key: crm.contact-types.read
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.contact-types.create
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.contact-types.update
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.contact-types.delete
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.organization-types.read
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.organization-types.create
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.organization-types.update
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.organization-types.delete
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.address-types.read
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.address-types.create
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.address-types.update
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.address-types.delete
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.phone-number-types.read
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.phone-number-types.create
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.phone-number-types.update
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
  - key: crm.phone-number-types.delete
    where_checked: api
    scenario: ATS-CRM-ENT-018-16
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md
related_coverage:
  - capability: CAP-CRM-ENT-001
    scenarios:
      - ATS-CRM-ENT-018-09
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md:related
  - capability: CAP-CRM-ENT-002
    scenarios:
      - ATS-CRM-ENT-018-10
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md:related
  - capability: CAP-CRM-ENT-005
    scenarios:
      - ATS-CRM-ENT-018-11
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md:related
  - capability: CAP-CRM-ENT-012
    scenarios:
      - ATS-CRM-ENT-018-12
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md:related
  - capability: CAP-CRM-ENT-015
    scenarios:
      - ATS-CRM-ENT-018-13
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md:related
  - capability: CAP-CRM-ENT-017
    scenarios:
      - ATS-CRM-ENT-018-14
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md:related
  - capability: CAP-CRM-ENT-019
    scenarios:
      - ATS-CRM-ENT-018-15
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-018-maintain-the-tenant-s-contact-organization-address-and-phone.md:related
examination_evidence:
  provenance: documented
  source: 'rhythm-v2-docs:specs/crm/entities/reports/EXAMINATION-2026-10-23.md'
  heading_conflict: FND-CRM-ENT-075
---

# Organize the workspace’s record classifications
## Coverage

The category map separates specified cases from recorded gaps. A proposed case describes proof to implement; a binding identifies the assertions supplied by an existing executable file. The permission and transition ledgers record the individual checks needed for the capability.

| Required rule | Scenario or finding |
|---|---|
| `BR-CAP-CRM-ENT-018-01` | `ATS-CRM-ENT-018-01` |
| `BR-CAP-CRM-ENT-018-02` | `ATS-CRM-ENT-018-02` |
| `BR-CAP-CRM-ENT-018-03` | `ATS-CRM-ENT-018-03`, `ATS-CRM-ENT-018-10`, `ATS-CRM-ENT-018-14` |
| `BR-CAP-CRM-ENT-018-04` | `ATS-CRM-ENT-018-04` |
| `BR-CAP-CRM-ENT-018-05` | `ATS-CRM-ENT-018-05`, `ATS-CRM-ENT-018-11` |
| `BR-CAP-CRM-ENT-018-06` | `ATS-CRM-ENT-018-06`, `ATS-CRM-ENT-018-16` |
| `BR-CAP-CRM-ENT-018-07` | `ATS-CRM-ENT-018-07`, `ATS-CRM-ENT-018-12`, `ATS-CRM-ENT-018-13`, `ATS-CRM-ENT-018-15` |

## Interactions With Related Capabilities

The interaction scenarios exercise establish a person’s CRM record; establish an organization and its hierarchy; maintain reliable contact points; review organization role compliance and prepare notices; control record locks and system protection; configure affiliation types and contact roles; resolve the forms and layouts used for CRM records. They change one part of a prepared record and inspect the dependent outcome. Unbound interactions remain proposed rather than implying an existing end-to-end implementation.

## Source Evidence

The frontmatter pins the repositories and identifies the source anchors for each scenario. Bindings record inspected assertions, while unbound cases remain proposed. Source inspection supplies no execution result or product approval.
