# CEM CRM Ring Popup Adapter

Phase 9C1 implements `POPUP_RING` through the existing MarvelPBX TCP-to-WebSocket bridge. It is disabled by default and is intended only for a controlled DEV rollout. The legacy listener remains authoritative outside an explicit, valid CEM-owned ring scope. Related architecture: MPBX-002, MPBX-004, MPBX-006 and MPBX-CONF-001.

## Existing bridge contract

The legacy listener opens a TCP connection to its reviewed bridge host and port, writes one JSON object followed by `\n`, and closes the connection. The bridge parses complete newline-delimited records and targets `event=ring` by `extension`. The existing browser client consumes:

- `event` = `ring`;
- `uuid` as call UI context;
- `extension` as the target;
- `number` for display/contact search;
- `contact_id`, `name`, `organization`, and `openTickets` when available.

Phase 9C1 preserves those field names and framing. Canonical `correlation_id` supplies `uuid`; no provider AMI identifier is exposed. Phase 9C1.3 supplies `contact_id`, contact display name, organization display name and open-ticket count only from one unambiguous read-only Vtiger match. The numeric CRM identifier exists solely for compatibility with the browser's contact-detail navigation and is not canonical CEM identity.

Phase 9C1.1 resolves the live ring-context blocker at the PBX adapter boundary. A reliable Grandstream/Asterisk destination channel is converted into canonical `payload.agent_extension`; the popup planner no longer needs to interpret `DestChannel`. Source tenant, customer, deployment and environment are supplied explicitly to the live follower rather than inherited from test defaults.

Phase 9C1.2b propagates explicit AMI `Queue` evidence through the call aggregate. In the validated DEV ordering, `QueueCallerJoin` and `AgentCalled` provide the PBX identity before `DialBegin`, so canonical ringing carries both `queue_id=6500` and `agent_extension=3000`. The PBX identity is not renamed to a CRM display label; local CRM policy maps that canonical ID to its allowed extensions. `Exten=s`, `Context`, `Application`, `ConnectedLineNum`, caller identity and channel text remain rejected queue heuristics. A capture without explicit timely queue evidence remains insufficient under the monitored-queue policy.

## Transport and privacy

The destination comes only from reviewed local configuration, never an event. Host, port and bounded connect/write timeouts are validated. Only a complete frame followed by a successful flush is accepted. Connection refusal, timeout, zero/partial unresolved writes and flush failures are retryable. Invalid configuration, extension, caller context or unsupported action type is terminal.

The bridge protocol provides no application acknowledgement. `COMMITTED` therefore means the complete frame was accepted by the local socket according to the same transport boundary used by the legacy listener; it does not prove that a browser displayed the popup.

Caller identity may be copied transiently from validated canonical payload into the bridge frame because the current UI uses it. It remains outside action audit, authority audit and idempotency state and is never a canonical identity. Full canonical events, AMI payloads, credentials and CRM record dumps are never sent.

Contact and organization display values share that transient boundary: they exist in the read-only lookup result, action popup context and bridge frame only. They are never added to CEM, the Event Journal, checkpoints, idempotency state, action/authority audits or metrics. `NOT_FOUND`, `AMBIGUOUS` and `UNAVAILABLE` lookups do not guess identity and retain the legacy-compatible unknown popup with empty display fields and a null `contact_id`.

Phase 9C1.4 supplies the lookup input as canonical ringing `caller_identity`, normalized only from inbound root-leg `CallerIDNum`. Unlike CRM display enrichment, this caller value is part of the canonical payload and is consequently present in canonical JSONL and the Event Journal; active aggregate persistence also retains it across restart. This reviewed DEV behavior is the minimum current mechanism for replayable CRM enrichment. The value remains transient after consumer read and is excluded from action audit, authority audit and idempotency state. A unique match enriches the initial popup; `NOT_FOUND`, `AMBIGUOUS` or `UNAVAILABLE` still sends the number with unknown contact fields and never suppresses the eligible popup.

## Authority and idempotency

Execution requires all of:

```text
consumer enabled
AND authority = CEM
AND fresh snapshot interlock enabled
AND adapter enabled
AND popup adapter type/enabled
AND idempotency permits execution
```

The logical identity remains `crm-popup-ring:{correlation_id}:{agent_extension}`. `EXECUTING` is persisted before transport. A complete write becomes `COMMITTED`; retryable failure does not. A committed action is not sent again, and an interrupted `EXECUTING` action requires reconciliation.

## Controlled DEV procedure

1. Verify the existing bridge runtime and browser client without changing their protocol.
2. Enable the production consumer, global adapter gate, and TCP popup adapter in ignored DEV configuration.
3. Keep every non-popup adapter disabled.
4. Configure one authority rule only: tenant `dev-local`, environment `DEV`, action `POPUP_RING`, extension `3000`, mode `CEM`; enable the snapshot interlock and export a fresh snapshot.
5. Run the consumer in follow mode over the real journal and place one controlled inbound call.
6. Verify legacy ring suppression, one CEM transport attempt, one bridge frame, one browser popup and one committed logical action.
7. Replay/restart and confirm no second frame.

Socket success alone is insufficient rollout evidence; the authority, audit, bridge, browser and replay observations must agree.

## Rollback

Restore the scope to `LEGACY`, set the interlock false and export a fresh snapshot. CEM cannot execute even if its adapter remains configured, while the listener immediately resumes ring delivery without restart. No database or call-state migration is required.
