---
id: STATE-CRM-ENT-006
title: Shared contact detail source
object: DOM-CRM-ENT-001
product_area: crm
module: entities
revision: 1
status: draft
design_intent: confirmed
origin: >-
  D-25 (Andrew, 2026-10-19) and CRM-M1 plan A11, cited in
  services-crm:docs/disconnected-information.md
implementation_evidence: observed
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
states:
  - name: owned
    meaning: The contact maintains its own contact-point value.
    entry_conditions: The contact stores independent value fields.
    allowed_actions:
      - Read
      - edit
      - link or remove under contact guards.
    terminal: false
    reversible: true
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#is
      refused 409 on a row that is no longer disconnected
    experience: The displayed value is maintained for this person.
  - name: copied
    meaning: >-
      The contact stores an organization snapshot with copy provenance,
      independent of later source edits.
    entry_conditions: A save keeps a source snapshot and copy provenance.
    allowed_actions:
      - Read
      - edit independently
      - change source mode under save validation.
    terminal: false
    reversible: true
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#makes
      an independent copy of the resolved value when the row is saved as copied
      with no value of its own
    experience: The value does not follow later organizational edits.
  - name: linked
    meaning: >-
      The contact references an organization source through an effective
      affiliation and reads its current value.
    entry_conditions: A saved source reference uses an effective affiliation.
    allowed_actions:
      - Read resolved source values
      - copy
      - relink under save guards.
    terminal: false
    reversible: true
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a link to a record of an organization other than the affiliation’s
    experience: The current organization value supplies the person’s display.
  - name: linked unresolved
    meaning: >-
      The saved link has no effective affiliation today, so reads resolve no
      value until eligibility or the link changes.
    entry_conditions: The saved link’s affiliation is not effective on the workspace day.
    allowed_actions:
      - Inspect its source
      - change the tie or detail under ordinary guards.
    terminal: false
    reversible: true
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#writes
      nothing to the contact: its version is the one before
    experience: Read resolution supplies no value; the source reference still exists.
  - name: disconnected
    meaning: >-
      The source was removed; the contact retains the last known value and
      disconnection metadata for review.
    entry_conditions: >-
      Source removal or organization merge retains its last values and service
      provenance.
    allowed_actions:
      - Read the warning
      - 'keep, relink or remove if the contact is editable.'
    terminal: false
    reversible: true
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#turns
      the linked work address into a disconnected row holding its last value,
      not a copy (D-25)
    experience: 'Verification is required and source, cause and time explain the warning.'
  - name: removed
    meaning: The contact no longer stores the detail.
    entry_conditions: The valid contact write deletes this detail row.
    allowed_actions:
      - Inspect its earlier values in authorized contact history.
    terminal: true
    reversible: false
    provenance: observed
    source: 'services-crm:docs/disconnected-information.md#What changed'
    experience: No live entry is displayed for that row.
transitions:
  - from: owned
    to: linked
    trigger: Link an owned detail or apply a type mapping to an empty purpose.
    guard: >-
      The saved affiliation is effective, the source belongs to its organization
      and any echoed value matches after whitespace normalization.
    side_effects:
      - >-
        resolve value from the source on reads without rewriting the contact for
        source edits
    who: An authorized editor or default-link settlement
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a link to a record of an organization other than the affiliation’s
    audit_event: >-
      Contact writes and source-removal disconnections capture contact revisions
      and advance the owning version. Date-only resolution changes do not create
      a contact revision because no stored contact value changes.
  - from: linked
    to: copied
    trigger: Change a live link to a copied detail without an independent value.
    guard: Use ordinary contact-save guards.
    side_effects:
      - store the resolved snapshot with organization and copy time
    who: An authorized contact editor
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#makes
      an independent copy of the resolved value when the row is saved as copied
      with no value of its own
    audit_event: >-
      Contact writes and source-removal disconnections capture contact revisions
      and advance the owning version. Date-only resolution changes do not create
      a contact revision because no stored contact value changes.
  - from: linked
    to: linked unresolved
    trigger: Pass the affiliation end or change it to a future period.
    guard: Calendar eligibility changes without a dedicated detail write.
    side_effects:
      - return no resolved value until the affiliation is effective again
    who: The calendar or an authorized relationship editor
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#writes
      nothing to the contact: its version is the one before
    audit_event: >-
      Contact writes and source-removal disconnections capture contact revisions
      and advance the owning version. Date-only resolution changes do not create
      a contact revision because no stored contact value changes.
  - from: linked
    to: disconnected
    trigger: Remove the organization source or merge away its organization.
    guard: The database removal trigger runs in the same transaction.
    side_effects:
      - retain last known value and source organization
      - record disconnection time and cause
      - surface verification required
      - advance affected contact versions and capture revisions
    who: The source-removal transaction
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#turns
      the linked work address into a disconnected row holding its last value,
      not a copy (D-25)
    audit_event: >-
      Contact writes and source-removal disconnections capture contact revisions
      and advance the owning version. Date-only resolution changes do not create
      a contact revision because no stored contact value changes.
  - from: disconnected
    to: owned
    trigger: Keep a disconnected value through the dedicated API or console patch.
    guard: >-
      Dedicated action requires update permission, current version, a
      still-disconnected row and an editable contact; console patch uses
      ordinary save checks.
    side_effects:
      - clear disconnection metadata
      - keep an owned value
      - advance version and capture revision
    who: An authorized contact editor
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#is
      refused 409 on a row that is no longer disconnected
    audit_event: >-
      Contact writes and source-removal disconnections capture contact revisions
      and advance the owning version. Date-only resolution changes do not create
      a contact revision because no stored contact value changes.
  - from: disconnected
    to: linked
    trigger: Relink through the dedicated API or console dialog.
    guard: >-
      Dedicated checks apply; choose exactly one valid source on an effective
      affiliation. Console uses general patch validation.
    side_effects:
      - store the new live reference
      - clear disconnection metadata
    who: An authorized contact editor
    provenance: documented
    source: 'services-crm:docs/disconnected-information.md#What changed'
    audit_event: >-
      Contact writes and source-removal disconnections capture contact revisions
      and advance the owning version. Date-only resolution changes do not create
      a contact revision because no stored contact value changes.
  - from: disconnected
    to: removed
    trigger: Remove through the dedicated API or console patch.
    guard: >-
      Dedicated action validates disconnection and version; console follows
      ordinary collection patch checks.
    side_effects:
      - delete the contact detail
    who: An authorized contact editor
    provenance: documented
    source: 'services-crm:docs/disconnected-information.md#What changed'
    audit_event: >-
      Contact writes and source-removal disconnections capture contact revisions
      and advance the owning version. Date-only resolution changes do not create
      a contact revision because no stored contact value changes.
  - from: owned
    to: removed
    trigger: Leave the detail out of an explicitly saved collection.
    guard: Any element lock blocking removal must be released first.
    side_effects:
      - >-
        select the earliest eligible nonseasonal address in type order if the
        preferred address was removed
    who: An authorized contact editor
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#empties
      the collection
    audit_event: >-
      Contact writes and source-removal disconnections capture contact revisions
      and advance the owning version. Date-only resolution changes do not create
      a contact revision because no stored contact value changes.
invalid:
  - from: owned
    to: disconnected
    why: >-
      Clients cannot create disconnection metadata; that provenance is written
      by the service.
    response: >-
      A client-created disconnected mode or forged disconnection provenance is
      refused. Only the service records the organization, time and cause of
      source loss.
  - from: owned
    to: linked
    why: >-
      Creating or redirecting a live link requires an affiliation effective
      today.
    response: >-
      A new live link through a future or ended affiliation is refused with
      contact-information validation. The caller must choose an effective
      relationship and a source belonging to it.
  - from: copied
    to: disconnected
    why: >-
      Copied values are independent snapshots and do not become disconnected
      when the source is removed.
    response: >-
      Source removal does not alter an independent copied snapshot into
      disconnected information. The copied row remains independent and receives
      no such transition.
concurrency: >-
  Source removal and all resulting contact disconnections commit together,
  advancing affected contact versions. Dedicated resolution actions verify both
  the read version and disconnected state. The console instead patches the
  contact collection and therefore relies on general version and collection
  checks.
invariants:
  - INV-DOM-CRM-ENT-001-10
  - INV-DOM-CRM-ENT-001-11
  - INV-DOM-CRM-ENT-003-12
evidence:
  - >-
    services-crm at 7ea48a41: docs/disconnected-information.md (D-25),
    docs/entities-api.md (Where an address or a phone number comes from);
    modules/entities/aws/api/disconnected-information.ts;
    db/contact-information-effective.view.ts; migration
    20261019210829_disconnected_contact_information.
  - >-
    react-console at da630758:
    components/record-views/cards/contact/disconnected-row.tsx (the actions
    write through the contact patch).
automatic:
  - from: linked
    to: linked unresolved
    when: >-
      The workspace date passes the affiliation’s inclusive end, without a
      detail write.
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#writes
      nothing to the contact: its version is the one before
  - from: linked
    to: disconnected
    when: An organization source is removed or its organization is merged away.
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#turns
      the linked work address into a disconnected row holding its last value,
      not a copy (D-25)
ai_may_recommend: false
ai_may_execute: false
permissions_by_state:
  - state: owned
    action: read
    permission: crm.contacts.read
  - state: owned
    action: update
    permission: crm.contacts.update
  - state: copied
    action: read
    permission: crm.contacts.read
  - state: copied
    action: update
    permission: crm.contacts.update
  - state: linked
    action: read
    permission: crm.contacts.read
  - state: linked
    action: update
    permission: crm.contacts.update
  - state: linked unresolved
    action: read
    permission: crm.contacts.read
  - state: linked unresolved
    action: update
    permission: crm.contacts.update
  - state: disconnected
    action: read
    permission: crm.contacts.read
  - state: disconnected
    action: update
    permission: crm.contacts.update
  - state: removed
    action: history
    permission: crm.contacts.history
narrative:
  status: verified
  review: NARRATIVE-REVIEW-2026-10-23
narrative_sources:
  - section: Purpose
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a link to a record of an organization other than the affiliation’s;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#makes
      an independent copy of the resolved value when the row is saved as copied
      with no value of its own;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#writes
      nothing to the contact: its version is the one before;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#turns
      the linked work address into a disconnected row holding its last value,
      not a copy (D-25);
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#is
      refused 409 on a row that is no longer disconnected;
      services-crm:docs/disconnected-information.md#What changed;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#empties
      the collection
  - section: Ownership and Authority
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a link to a record of an organization other than the affiliation’s;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#makes
      an independent copy of the resolved value when the row is saved as copied
      with no value of its own;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#writes
      nothing to the contact: its version is the one before;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#turns
      the linked work address into a disconnected row holding its last value,
      not a copy (D-25);
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#is
      refused 409 on a row that is no longer disconnected;
      services-crm:docs/disconnected-information.md#What changed;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#empties
      the collection
  - section: The Lifecycle
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a link to a record of an organization other than the affiliation’s;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#makes
      an independent copy of the resolved value when the row is saved as copied
      with no value of its own;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#writes
      nothing to the contact: its version is the one before;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#turns
      the linked work address into a disconnected row holding its last value,
      not a copy (D-25);
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#is
      refused 409 on a row that is no longer disconnected;
      services-crm:docs/disconnected-information.md#What changed;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#empties
      the collection
  - section: States
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a link to a record of an organization other than the affiliation’s;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#makes
      an independent copy of the resolved value when the row is saved as copied
      with no value of its own;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#writes
      nothing to the contact: its version is the one before;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#turns
      the linked work address into a disconnected row holding its last value,
      not a copy (D-25);
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#is
      refused 409 on a row that is no longer disconnected;
      services-crm:docs/disconnected-information.md#What changed;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#empties
      the collection
  - section: Transitions Explained
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a link to a record of an organization other than the affiliation’s;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#makes
      an independent copy of the resolved value when the row is saved as copied
      with no value of its own;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#writes
      nothing to the contact: its version is the one before;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#turns
      the linked work address into a disconnected row holding its last value,
      not a copy (D-25);
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#is
      refused 409 on a row that is no longer disconnected;
      services-crm:docs/disconnected-information.md#What changed;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#empties
      the collection
  - section: What Cannot Happen
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a link to a record of an organization other than the affiliation’s;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#makes
      an independent copy of the resolved value when the row is saved as copied
      with no value of its own;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#writes
      nothing to the contact: its version is the one before;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#turns
      the linked work address into a disconnected row holding its last value,
      not a copy (D-25);
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#is
      refused 409 on a row that is no longer disconnected;
      services-crm:docs/disconnected-information.md#What changed;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#empties
      the collection
  - section: Concurrency and Timing
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a link to a record of an organization other than the affiliation’s;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#makes
      an independent copy of the resolved value when the row is saved as copied
      with no value of its own;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#writes
      nothing to the contact: its version is the one before;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#turns
      the linked work address into a disconnected row holding its last value,
      not a copy (D-25);
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#is
      refused 409 on a row that is no longer disconnected;
      services-crm:docs/disconnected-information.md#What changed;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#empties
      the collection
  - section: Permissions by State
    provenance: documented
    source: 'rhythm-v2-docs:specs/crm/AUTHORIZATION.md#Role Entitlement Matrix'
  - section: Purpose
    provenance: inferred
    source: >-
      services-crm at 7ea48a41: docs/disconnected-information.md (D-25),
      docs/entities-api.md (Where an address or a phone number comes from);
      modules/entities/aws/api/disconnected-information.ts;
      db/contact-information-effective.view.ts; migration
      20261019210829_disconnected_contact_information.
    note: >-
      The explicitly labeled rationale interprets the separation of state
      dimensions and the existing trigger and guard rows.
  - section: Invariants
    provenance: inferred
    source: INV-DOM-CRM-ENT-001-10; INV-DOM-CRM-ENT-001-11; INV-DOM-CRM-ENT-003-12
    note: >-
      Protection explanations interpret the unchanged domain-object invariants;
      no lifecycle policy is added.
  - section: Invariants
    provenance: inferred
    source: "services-crm:modules/entities/services/persistence/contacts.ts; services-crm:modules/entities/services/persistence/organizations.ts; services-crm:modules/entities/services/organizations/compliance/organization-compliance.ts; services-crm:modules/entities/services/persistence/record-locks.ts"
    note: "The rationale interprets this artifact’s sourced invariants and linked capability rules; it does not claim a recorded product decision."
---

# Shared contact detail source

## Purpose

This lifecycle explains whether a contact point is independently maintained, copied, actively supplied by an organization or waiting for review after its source disappears. A work address can look plausible in every case, so staff need its source meaning to decide whether it is current and who should correct it.

The recorded disconnection decision preserves the last known value with an explanation instead of quietly turning source loss into a user-confirmed copy. Our reading is that distinguishing unresolved links from removed sources also prevents an ended employment period from being misread as an organization deleting its address.

## Ownership and Authority

Contact editors choose permitted ownership, copying or linking through the contact save. The source-removal transaction alone establishes disconnected provenance. Authorized staff can later keep, relink or remove the value. Dedicated resolution APIs check the still-disconnected state as well as version; the console currently uses a general version-tested collection patch.

The calendar can change whether a saved link resolves without changing its storage mode. Reconciliation can apply an eligible saved default to an empty purpose. No inference operation makes or executes a source choice; readers are shown the provenance needed to interpret the information themselves.

## The Lifecycle


```mermaid
stateDiagram-v2
  state "Linked but unresolved" as unresolved
  owned --> linked: choose effective source
  linked --> copied: take snapshot
  linked --> unresolved: affiliation not effective
  unresolved --> linked: affiliation effective again
  linked --> disconnected: source removed
  disconnected --> owned: keep retained value
  disconnected --> linked: choose new effective source
  disconnected --> removed: remove detail
```

## States

### owned

The contact maintains its own contact-point value. Entry requires this condition: The contact stores independent value fields. Available behavior is Read; edit; link or remove under contact guards.. The displayed value is maintained for this person.

### copied

The contact stores an organization snapshot with copy provenance, independent of later source edits. Entry requires this condition: A save keeps a source snapshot and copy provenance. Available behavior is Read; edit independently; change source mode under save validation.. The value does not follow later organizational edits.

### linked

The contact references an organization source through an effective affiliation and reads its current value. Entry requires this condition: A saved source reference uses an effective affiliation. Available behavior is Read resolved source values; copy; relink under save guards.. The current organization value supplies the person’s display.

### linked unresolved

The saved link has no effective affiliation today, so reads resolve no value until eligibility or the link changes. Entry requires this condition: The saved link’s affiliation is not effective on the workspace day. Available behavior is Inspect its source; change the tie or detail under ordinary guards.. Read resolution supplies no value; the source reference still exists.

### disconnected

The source was removed; the contact retains the last known value and disconnection metadata for review. Entry requires this condition: Source removal or organization merge retains its last values and service provenance. Available behavior is Read the warning; keep, relink or remove if the contact is editable.. Verification is required and source, cause and time explain the warning.

### removed

The contact no longer stores the detail. Entry requires this condition: The valid contact write deletes this detail row. Available behavior is Inspect its earlier values in authorized contact history.. No live entry is displayed for that row. This row’s lifecycle ends here; a new request or new record is separate work.

## Transitions Explained

### owned → linked

An authorized editor or default-link settlement starts this change when the following occurs: Link an owned detail or apply a type mapping to an empty purpose. The transition checks this boundary: The saved affiliation is effective, the source belongs to its organization and any echoed value matches after whitespace normalization. The accompanying work is resolve value from the source on reads without rewriting the contact for source edits. Contact writes and source-removal disconnections capture contact revisions and advance the owning version. Date-only resolution changes do not create a contact revision because no stored contact value changes.

### linked → copied

An authorized contact editor starts this change when the following occurs: Change a live link to a copied detail without an independent value. The transition checks this boundary: Use ordinary contact-save guards. The accompanying work is store the resolved snapshot with organization and copy time. Contact writes and source-removal disconnections capture contact revisions and advance the owning version. Date-only resolution changes do not create a contact revision because no stored contact value changes.

### linked → linked unresolved

The calendar or an authorized relationship editor starts this change when the following occurs: Pass the affiliation end or change it to a future period. The transition checks this boundary: Calendar eligibility changes without a dedicated detail write. The accompanying work is return no resolved value until the affiliation is effective again. Contact writes and source-removal disconnections capture contact revisions and advance the owning version. Date-only resolution changes do not create a contact revision because no stored contact value changes.

### linked → disconnected

The source-removal transaction starts this change when the following occurs: Remove the organization source or merge away its organization. The transition checks this boundary: The database removal trigger runs in the same transaction. The accompanying work is retain last known value and source organization; record disconnection time and cause; surface verification required; advance affected contact versions and capture revisions. Contact writes and source-removal disconnections capture contact revisions and advance the owning version. Date-only resolution changes do not create a contact revision because no stored contact value changes.

### disconnected → owned

An authorized contact editor starts this change when the following occurs: Keep a disconnected value through the dedicated API or console patch. The transition checks this boundary: Dedicated action requires update permission, current version, a still-disconnected row and an editable contact; console patch uses ordinary save checks. The accompanying work is clear disconnection metadata; keep an owned value; advance version and capture revision. Contact writes and source-removal disconnections capture contact revisions and advance the owning version. Date-only resolution changes do not create a contact revision because no stored contact value changes.

### disconnected → linked

An authorized contact editor starts this change when the following occurs: Relink through the dedicated API or console dialog. The transition checks this boundary: Dedicated checks apply; choose exactly one valid source on an effective affiliation. Console uses general patch validation. The accompanying work is store the new live reference; clear disconnection metadata. Contact writes and source-removal disconnections capture contact revisions and advance the owning version. Date-only resolution changes do not create a contact revision because no stored contact value changes.

### disconnected → removed

An authorized contact editor starts this change when the following occurs: Remove through the dedicated API or console patch. The transition checks this boundary: Dedicated action validates disconnection and version; console follows ordinary collection patch checks. The accompanying work is delete the contact detail. Contact writes and source-removal disconnections capture contact revisions and advance the owning version. Date-only resolution changes do not create a contact revision because no stored contact value changes.

### owned → removed

An authorized contact editor starts this change when the following occurs: Leave the detail out of an explicitly saved collection. The transition checks this boundary: Any element lock blocking removal must be released first. The accompanying work is select the earliest eligible nonseasonal address in type order if the preferred address was removed. Contact writes and source-removal disconnections capture contact revisions and advance the owning version. Date-only resolution changes do not create a contact revision because no stored contact value changes.

## What Cannot Happen

A client-created disconnected mode or forged disconnection provenance is refused. Only the service records the organization, time and cause of source loss.

A new live link through a future or ended affiliation is refused with contact-information validation. The caller must choose an effective relationship and a source belonging to it.

Source removal does not alter an independent copied snapshot into disconnected information. The copied row remains independent and receives no such transition.

## Permissions by State

The state rows reference grants from the CRM authorization model. Read access is distinct from mutation, and history requires the corresponding history and read checks. Administrator and Power User hold administrative record actions; ordinary Standard User grants include applicable read and update operations, while Read Only grants do not authorize edits.

Contact update controls these nested corrections even when initiated from an organization. Archive and locks can still refuse an otherwise authorized change. Calendar interpretation needs no user grant.

## Concurrency and Timing

An organization-source removal and all dependent disconnections commit in one database transaction. Each affected contact’s version advances, so an open editor can conflict even though its owner did not initiate the organization change. Failure of disconnection capture rolls back source removal rather than leaving broken live references.

Dedicated recovery compares both the read version and the disconnected state, preventing a stale action from resolving an already-settled row. The console’s general patch has a different validation boundary. Calendar expiry alone changes read resolution without rewriting contact values, stamps or history.

## Invariants

Our reading is that source loss and an ineffective affiliation must remain distinguishable. An ended tie can stop a link from resolving without deleting the organization’s detail or creating a disconnected snapshot. Actual source removal retains the last known value and service provenance. Keeping it later establishes owned information; relinking selects a new valid source. Version and still-disconnected checks protect dedicated recovery from silently replacing another editor’s already completed resolution.
