---
id: CAP-CRM-ENT-011
title: Maintain responsibilities on organizational relationships
product_area: crm
module: entities
revision: 1
status: approved
product_intent: proposed
implementation_evidence: inconsistent
source_sha:
  services-crm: 7ea48a414c42eade10d60753b8bcc323b1dd726f
  react-console: da63075876b2c5f4a6af07d457bb24ea8bf4c0c7
intent_sources:
  - 'services-crm:docs/milestones/CRM-M1-contacts-and-organizations.md#M1-D1'
  - 'services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D2'
  - 'services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D6'
  - 'services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D12'
  - 'services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D13'
  - 'services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.plan.md#A6'
purpose: >-
  Staff assign organizational responsibilities and configure automatic
  assignment where appropriate.
outcomes:
  - 'Each role is a dated row on one affiliation, never outliving it.'
  - >-
    A person who joins an organization receives its automatic roles, and its
    first person the first-contact roles.
  - >-
    An organization type's role restriction is enforced by the service and the
    pickers.
actors:
  - ACT-CRM-001
  - ACT-CRM-003
  - ACT-CRM-004
  - ACT-CRM-005
surfaces:
  - console
  - api
  - job
preconditions: []
postconditions: []
permissions:
  - key: crm.contacts.update
    where_checked: api
    scope: tenant
  - key: crm.contact-roles.read
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-CRM-ENT-011-01
    proved_by: [ATS-CRM-ENT-011-01, ATS-CRM-ENT-011-12, TST-CRM-ENT-786, TST-CRM-ENT-790]
    statement: >-
      Affiliation roles must reference a contact role in the tenant, whether
      active or inactive, and reject unknown or foreign roles.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: observed
    source: >-
      services-crm:modules/entities/services/persistence/contact-affiliations.ts:affiliationRefusal
    origin: none
  - id: BR-CAP-CRM-ENT-011-02
    proved_by: [ATS-CRM-ENT-011-01, TST-CRM-ENT-786, TST-CRM-ENT-790]
    statement: >-
      Explicit role boundaries must fit within the affiliation period, while
      omitted boundaries inherit the corresponding affiliation boundary.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#refuses
      a role whose dates lie outside its affiliation's
    origin: none
  - id: BR-CAP-CRM-ENT-011-03
    proved_by: [ATS-CRM-ENT-011-02, TST-CRM-ENT-776, TST-CRM-ENT-790]
    statement: >-
      A role cannot overlap another period of the same role on one affiliation,
      may recur in separate periods or separate affiliations, and an affiliation
      supports at most 25 role rows.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#refuses
      the same role overlapping itself on one affiliation
    origin: 'services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D6'
  - id: BR-CAP-CRM-ENT-011-04
    proved_by: [ATS-CRM-ENT-009-22, ATS-CRM-ENT-011-03, ATS-CRM-ENT-011-13, TST-CRM-ENT-786]
    statement: >-
      Changing an affiliation end closes its open or later-ending role periods
      on that day, while an unchanged affiliation end leaves those periods
      alone.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#ends
      the open roles of an affiliation the save ends, on the same day
    origin: none
  - id: BR-CAP-CRM-ENT-011-05
    proved_by: [ATS-CRM-ENT-011-04, ATS-CRM-ENT-011-15, ATS-CRM-ENT-012-22, TST-CRM-ENT-776, TST-CRM-ENT-790]
    statement: >-
      New or changed roles must satisfy the organization type’s restriction with
      a refusal naming the role, type and allowed list; unchanged roles bypass
      rechecking and pickers retain held roles alongside permitted choices.
    kind: validation
    enforced_at:
      - service
      - console
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-affiliations.integration.spec.js#refuses
      a role outside the list added to an existing affiliation
    origin: 'services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D2'
  - id: BR-CAP-CRM-ENT-011-06
    proved_by: [ATS-CRM-ENT-011-05, ATS-CRM-ENT-011-16, ATS-CRM-ENT-017-14, TST-CRM-ENT-776, TST-CRM-ENT-790]
    statement: >-
      Moving a contact’s primary organization assigns active automatic roles not
      already held on the new primary tie and first-contact roles when no other
      current affiliate exists, while an unchanged primary organization assigns
      nothing.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/contacts/role-assignment/contact-role-assignment.integration.spec.js#gives
      the organization's first contact the automatic and first-contact roles,
      besides the roles it states
    origin: none
  - id: BR-CAP-CRM-ENT-011-07
    proved_by: [ATS-CRM-ENT-010-11, ATS-CRM-ENT-011-06, ATS-CRM-ENT-011-14, TST-CRM-ENT-790]
    statement: >-
      Future affiliations receive automatic roles no earlier than their local
      start day through a later save or reconciliation, and corrections to start
      or end dates determine whether and when assignment occurs.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/contacts/role-assignment/automatic-role-reconciliation.integration.spec.js#gives
      the automatic and first-contact roles on the start day in the tenant
      calendar
    origin: >-
      services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.plan.md#A6
  - id: BR-CAP-CRM-ENT-011-08
    proved_by: [ATS-CRM-ENT-011-07, TST-CRM-ENT-790]
    statement: >-
      Automatic roles inherit the affiliation’s dates, are only added, are not
      restored after deliberate removal and wait when the contact or affiliation
      is locked against updates.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/contacts/role-assignment/automatic-role-reconciliation.integration.spec.js#does
      not give back a role a person removed after it was given
    origin: none
  - id: BR-CAP-CRM-ENT-011-09
    proved_by: [ATS-CRM-ENT-011-08, TST-CRM-ENT-774, TST-CRM-ENT-790]
    statement: >-
      Inactive and deceased contacts receive no new automatic roles, retain
      existing roles and still count as affiliates when deciding whether another
      contact is first.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/contacts/role-assignment/contact-role-assignment.integration.spec.js#counts
      an inactive contact as already belonging to the organization, so the next
      contact is not its first
    origin: 'services-crm:docs/milestones/CRM-M1-contacts-and-organizations.md#M1-D1'
  - id: BR-CAP-CRM-ENT-011-10
    proved_by: [ATS-CRM-ENT-011-09, TST-CRM-ENT-790]
    statement: >-
      Organization type restrictions suppress disallowed automatic roles and
      report the skipped roles with the restricting type.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/affiliations/affiliation-date-reconciliation.follow-ups.integration.spec.js#skips
      the forbidden role on the start day
    origin: 'services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D2'
  - id: BR-CAP-CRM-ENT-011-11
    proved_by: [ATS-CRM-ENT-011-10]
    statement: >-
      Rejoining an organization assigns automatic roles to the new affiliation
      again while retaining the old affiliation’s ended role history.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: documented
    source: >-
      services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D12
    origin: >-
      services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D12
  - id: BR-CAP-CRM-ENT-011-12
    proved_by: [ATS-CRM-ENT-011-11, TST-CRM-ENT-790]
    statement: >-
      Concurrent joins to an empty organization give the first-contact roles to
      exactly one joining contact.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/contacts/role-assignment/contact-role-assignment.integration.spec.js#gives
      the first-contact roles to exactly one of several contacts joining an
      empty organization at once
    origin: none
  - id: BR-CAP-CRM-ENT-011-13
    proved_by: []
    statement: >-
      The contact editor presents separate dated rows for repeated role periods
      and allows another period to be added.
    kind: policy
    enforced_at:
      - console
    must_prove: false
    provenance: observed
    source: 'react-console:components/affiliations/affiliation-editor.tsx:Add Period'
    origin: 'services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D6'
  - id: BR-CAP-CRM-ENT-011-14
    proved_by: []
    statement: >-
      The product should offer imports an Apply automatic roles option,
      initially off, in the imports milestone.
    kind: policy
    enforced_at: []
    must_prove: false
    provenance: documented
    source: >-
      services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D13
    origin: >-
      services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D13
scenarios:
  - id: SC-CAP-CRM-ENT-011-01
    given: >-
      A tenant with an automatic Member Contact role and a first-contact Billing
      Contact role.
    when: 'The first person joins a new company as primary, then a second.'
    then: 'The first holds both, the second only Member Contact.'
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/contacts/role-assignment/contact-role-assignment.integration.spec.js#gives
      a later contact only the automatic roles
  - id: SC-CAP-CRM-ENT-011-02
    given: 'Chapters restrict roles to President, Treasurer and Secretary.'
    when: A staff member gives someone Billing Contact at a chapter.
    then: The save is rejected naming the allowed roles.
    provenance: test
    source: >-
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0188.spec.ts#CRM-ACC-0188
edge_cases:
  - id: SC-CAP-CRM-ENT-011-03
    given: >-
      A non-primary affiliation added today and another starting tomorrow, both
      at organizations with automatic roles.
    when: The first is saved; the second reaches its start day.
    then: >-
      The first is given no automatic role; the second is given them by the
      reconciliation.
    provenance: unknown
    source: FND-CRM-ENT-008
constraints: []
depends_on:
  - CAP-CRM-ENT-009
  - CAP-CRM-ENT-017
related:
  - CAP-CRM-ENT-009
  - CAP-CRM-ENT-010
  - CAP-CRM-ENT-012
  - CAP-CRM-ENT-017
journeys:
  - JNY-CRM-ENT-002
open_questions: []
feature: FEAT-CRM-ENT-04
concepts:
  - CON-CRM-ENT-001
  - CON-CRM-ENT-002
  - CON-CRM-ENT-003
evidence:
  - >-
    `services-crm` at `7ea48a414c42eade10d60753b8bcc323b1dd726f`: examined
    `services/persistence/contact-affiliations.ts` (role rules, restrictions),
    `services/contacts/role-assignment/contact-role-assignment.ts`,
    `automatic-role-reconciliation.ts`, `db/contact-organization-roles.table.ts`; tests
    `unit/services/persistence/contact-affiliations.spec.js`,
    `integration/services/persistence/contact-affiliations.integration.spec.js`,
    `integration/services/contacts/role-assignment/*.integration.spec.js`,
    `integration/services/affiliations/affiliation-date-reconciliation.follow-ups.integration.spec.js`,
    `integration/db/contact-organization-roles.table.integration.spec.js`.
  - >-
    `react-console` at `da63075876b2c5f4a6af07d457bb24ea8bf4c0c7`: examined
    `components/affiliations/affiliation-editor.tsx`; inventory RUL-CRM-AFF-009, 019 to 022, 026 to
    029; journeys CRM-ACC-0031, 0182, 0183, 0184, 0188.
approved_by: Andrew
approved_at: 2026-10-22
controls: [TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-006, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-IAM-001, TC-IAM-005, TC-IAM-006, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-MON-002, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-TEN-001, TC-TEN-002, TC-TEN-003]
narrative:
  status: verified
  review: NARRATIVE-REVIEW-2026-10-23
narrative_sources:
  - section: Why It Works This Way
    provenance: inferred
    source: >-
      services-crm:modules/entities/services/persistence/contact-affiliations.ts:affiliationRefusal;
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#refuses
      a role whose dates lie outside its affiliation's;
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#refuses
      the same role overlapping itself on one affiliation;
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#ends
      the open roles of an affiliation the save ends, on the same day;
      services-crm:modules/entities/tests/integration/services/persistence/contact-affiliations.integration.spec.js#refuses
      a role outside the list added to an existing affiliation;
      services-crm:modules/entities/tests/integration/services/contacts/role-assignment/contact-role-assignment.integration.spec.js#gives
      the organization's first contact the automatic and first-contact roles,
      besides the roles it states;
      services-crm:modules/entities/tests/integration/services/contacts/role-assignment/automatic-role-reconciliation.integration.spec.js#gives
      the automatic and first-contact roles on the start day in the tenant
      calendar;
      services-crm:modules/entities/tests/integration/services/contacts/role-assignment/automatic-role-reconciliation.integration.spec.js#does
      not give back a role a person removed after it was given;
      services-crm:modules/entities/tests/integration/services/contacts/role-assignment/contact-role-assignment.integration.spec.js#counts
      an inactive contact as already belonging to the organization, so the next
      contact is not its first;
      services-crm:modules/entities/tests/integration/services/affiliations/affiliation-date-reconciliation.follow-ups.integration.spec.js#skips
      the forbidden role on the start day;
      services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D12;
      services-crm:modules/entities/tests/integration/services/contacts/role-assignment/contact-role-assignment.integration.spec.js#gives
      the first-contact roles to exactly one of several contacts joining an
      empty organization at once;
      react-console:components/affiliations/affiliation-editor.tsx:Add Period;
      services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D13
    note: >-
      Operational rationale inferred from the cited behavior; no personal motive
      or historical decision is asserted.
repositories:
  - repo: services-crm
    role: owner
    paths:
      - modules/entities/
      - docs/milestones/CRM-M2-affiliations-roles-compliance.md
      - modules/entities/services/persistence/contact-affiliations.ts
      - >-
        modules/entities/tests/integration/services/affiliations/affiliation-date-reconciliation.follow-ups.integration.spec.js
      - >-
        modules/entities/tests/integration/services/contacts/role-assignment/automatic-role-reconciliation.integration.spec.js
      - >-
        modules/entities/tests/integration/services/contacts/role-assignment/contact-role-assignment.integration.spec.js
      - >-
        modules/entities/tests/integration/services/persistence/contact-affiliations.integration.spec.js
      - >-
        modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js
  - repo: react-console
    role: console
    paths:
      - 'app/(console)/console/[tenantId]/crm/contacts/'
      - acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0188.spec.ts
      - components/affiliations/affiliation-editor.tsx
exercised_by: [FAS-CRM-ENT-04-01, FAS-CRM-ENT-04-02, FAS-CRM-ENT-04-03, FAS-CRM-ENT-04-04, FAS-CRM-ENT-04-05, FAS-CRM-ENT-05-01, FAS-CRM-ENT-05-02, FAS-CRM-ENT-05-05]
suites: [TST-CRM-ENT-774, TST-CRM-ENT-776, TST-CRM-ENT-786, TST-CRM-ENT-790]
---

# Maintain responsibilities on organizational relationships

## Purpose

An association needs to know what a person does for an organization and when that responsibility applies. This capability gives affiliations dated role periods and applies eligible automatic assignments, so a billing contact, officer, or returning representative can be understood without flattening responsibility into a timeless flag on the person.

## How It Works

Staff choose roles from the workspace catalogue and record periods within an affiliation. Missing boundaries inherit the affiliation's boundaries, and recurring periods remain separate dated entries. The same role cannot overlap itself within one tie, and the affiliation's role-row limit constrains the submitted set. Ending a tie also closes open or later-ending roles when that end changes.

An organization type can restrict allowed roles. New or changed roles are checked against it; unchanged held roles remain saveable and available alongside permitted choices. Automatic and first-contact roles can be added for an eligible new primary relationship. Future affiliations wait until their local start, and reconciliation or a later save can perform the assignment.

Automatic assignment only adds eligible roles and respects deliberate removal, contact status, locks, and type restrictions. Rejoining creates a fresh assignment opportunity on the new tie while preserving the old one. Concurrent joins to an empty organization give first-contact roles to one person, rather than every racing request. The imports automatic-role option remains proposed for the imports milestone.

## Example

A member firm's first representative joins with a primary affiliation and receives eligible automatic and first-contact roles. A second representative receives the general automatic roles only. The first representative later leaves and returns on a new affiliation. The new tie can receive automatic roles again, while the ended affiliation retains the earlier responsibility periods for staff inspecting past coverage.

At Harbor West Chapter, Ruth Kim's Chair responsibility ends October 23 and another period starts October 24. The coordinator records the gap or succession explicitly rather than changes the old role identity into a different duty. A role dated beyond its supporting affiliation is refused. If the chapter's type now permits Treasurer but no longer Chair, a changed Chair period must satisfy that restriction; an unchanged historical period retains its preservation boundary during an unrelated correction. The coordinator checks both held and permitted choices, then reads the saved periods. A future responsibility does not count as present compliance coverage merely because staff can already see it.

## Why It Works This Way

Our reading is that role periods belong to the relationship because a person can hold different responsibilities at different organizations. Inherited dates simplify ordinary entry without letting a responsibility outlive its supporting tie. Nonoverlap avoids duplicate coverage for the same role, while separate recurrence preserves a meaningful gap between terms.

Checking changed roles while retaining unchanged ones prevents a policy edit from blocking unrelated corrections to historical information. Automatic assignment respects deliberate removal because repeated reconciliation should not undo staff intent. First-contact serialization protects the special assignment from concurrency, and considering existing affiliates prevents an inactive person's presence from being ignored merely to create another first contact. Locked or ineligible people do not acquire roles simply because a worker repeats its scan.

## Relationships

Contact Role supplies the catalogue meaning, and Organization Type supplies restrictions and policy. Maintain a person’s organizational relationships owns the supporting tie. Keep dated relationships current processes start-day assignment and boundary effects. Review organization role compliance and prepare notices counts effective role holders, while classification administration maintains role definitions rather than individual responsibility periods.
