From 1a429d880055de56d164e6d5d447cbf17977971f Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 1 Jun 2026 23:59:23 +0200 Subject: [PATCH] Seeded repo with intent and specs --- INTENT.md | 224 ++ .../260601-AdapterInterfaceSpecification.md | 1342 +++++++++++ .../260601-HybridmailAdapterSpecification.md | 1787 ++++++++++++++ ...-RuntimeArchitectureAndAdapterSubsystem.md | 1412 +++++++++++ spec/AdapterInterfaceSpecification.md | 1991 ++++++++++++++++ spec/EmailAdapterSpecification.md | 1520 ++++++++++++ spec/HybridMailProviderApiComparison.md | 925 ++++++++ spec/HybridmailAdapterSpecification.md | 2076 +++++++++++++++++ spec/HybridmailBinectSpecification.md | 1057 +++++++++ spec/HybridmailEpostbusinessSpecification.md | 1220 ++++++++++ spec/HybridmailPingenSpecification.md | 1165 +++++++++ spec/ProductRequirementsDocument.md | 987 ++++++++ spec/RssAdapterSpecification.md | 1394 +++++++++++ .../RuntimeArchitectureAndAdapterSubsystem.md | 1776 ++++++++++++++ spec/SmsAdapterSpecification.md | 1699 ++++++++++++++ spec/XmppAdapterSpecification.md | 1621 +++++++++++++ 16 files changed, 22196 insertions(+) create mode 100644 INTENT.md create mode 100644 history/260601-AdapterInterfaceSpecification.md create mode 100644 history/260601-HybridmailAdapterSpecification.md create mode 100644 history/260601-RuntimeArchitectureAndAdapterSubsystem.md create mode 100644 spec/AdapterInterfaceSpecification.md create mode 100644 spec/EmailAdapterSpecification.md create mode 100644 spec/HybridMailProviderApiComparison.md create mode 100644 spec/HybridmailAdapterSpecification.md create mode 100644 spec/HybridmailBinectSpecification.md create mode 100644 spec/HybridmailEpostbusinessSpecification.md create mode 100644 spec/HybridmailPingenSpecification.md create mode 100644 spec/ProductRequirementsDocument.md create mode 100644 spec/RssAdapterSpecification.md create mode 100644 spec/RuntimeArchitectureAndAdapterSubsystem.md create mode 100644 spec/SmsAdapterSpecification.md create mode 100644 spec/XmppAdapterSpecification.md diff --git a/INTENT.md b/INTENT.md new file mode 100644 index 0000000..aeecd4f --- /dev/null +++ b/INTENT.md @@ -0,0 +1,224 @@ +# INTENT.md + +## Project Name + +**coordination-engine** + +## Purpose + +`coordination-engine` is a generalized framework and runtime for digital coordination as goal- and result-driven communication between multiple interacting parties. + +It provides the foundation for building scenario-specific, headless coordination applications by selecting and composing only the relevant concepts from the framework: participants, intended results, payloads, action surfaces, access, notifications, deliveries, interactions, evidence, policies, and adapters. + +The core idea is that digital coordination is not primarily about transporting messages from sender to receiver. It is about managing access, enabling interactions, observing evidence, and applying follow-up policies until an intended result is achieved, failed, expired, escalated, or manually closed. + +## Primary Utility + +`coordination-engine` helps developers and agents build applications that coordinate people, organizations, systems, payloads, and actions across multiple channels and technologies. + +It is intended for scenarios such as: + +* digital postal delivery +* secure document access +* document and data collection +* AGB / terms acceptance +* contract signing and closing +* invoice and payment collection +* incident acknowledgement +* approval workflows +* onboarding processes +* regulated communication and evidence tracking + +The engine should make these scenarios easier to implement by providing reusable coordination primitives instead of requiring every application to reinvent notification logic, delivery tracking, participant state, evidence handling, and follow-up rules. + +## Strategic Role + +`coordination-engine` is the implementation project for the broader **coordination-framework**. + +It should become a headless coordination runtime that can be embedded into or integrated with specialized applications. These applications may expose their own user interfaces, portals, workflows, APIs, or agent interfaces while relying on `coordination-engine` for the underlying coordination logic. + +The project is strategically positioned between notification systems, workflow engines, document delivery platforms, campaign automation tools, signature systems, payment systems, and evidence/audit infrastructure. + +It is not meant to replace all of these systems. Instead, it provides the coordination layer that can orchestrate them through adapters. + +## Core Principle + +A coordination case is successful when its intended result is satisfied, not when messages have merely been sent. + +Digital coordination is the controlled arrangement of participants, payloads, access, notifications, deliveries, interactions, and evidence so that intended results can be achieved under uncertainty. + +## Conceptual Scope + +The project revolves around the following core concepts. + +### CoordinationCase + +A goal-directed coordination process initiated by one party to achieve an intended result involving one or more participants. + +### Participant + +An actor involved in the coordination case, such as an initiator, recipient, respondent, signer, payer, approver, delegate, organization, system, agent, or intermediary. + +### IntendedResult + +The result the initiator wants to achieve, such as payload access, valid submission, acceptance, signature, payment, acknowledgement, or completion. + +### Payload + +A meaningful resource involved in coordination, such as a document, contract, invoice, form, payment request, data package, submitted artifact, or receipt. + +### ActionSurface + +The place where participants interact with the case, such as a portal page, app screen, payment page, signature flow, upload form, approval screen, chatbot, API endpoint, or message reply interface. + +### Notification + +An awareness-oriented communication act that prompts attention or action. + +### Delivery + +The controlled availability, access, transfer, retrieval, submission, or consumption of a payload. + +### Interaction + +A meaningful action performed by a participant, system, agent, or intermediary on a payload or action surface. + +### EvidenceEvent + +A normalized observation used to assess progress, uncertainty, success, failure, or required follow-up. + +### Policy + +A rule set that interprets evidence and determines next actions such as waiting, notifying, reminding, retrying, escalating, switching channels, requesting correction, revoking access, closing, or failing. + +## Intended Architecture Direction + +The runtime should be organized around a central **coordination-controller** supported by specialized controllers and services: + +* `coordination-controller` +* `result-controller` +* `participant-controller` +* `payload-controller` +* `access-controller` +* `notification-controller` +* `delivery-controller` +* `interaction-controller` +* `identity-authority-controller` +* `evidence-ledger` +* `policy-engine` +* `adapter-layer` + +The architecture should remain headless, API-first, event-aware, and adapter-friendly. + +## Scenario Composition + +The framework should allow scenario-specific applications to use only the concepts they need. + +A simple notification-and-access scenario should not need payment, signature, or document-validation functionality. + +A contract-signing scenario should be able to select stronger identity, authority, payload-integrity, signature, and evidence requirements. + +A payment-collection scenario should be able to integrate invoice payloads, payment action surfaces, payment-provider events, reminders, disputes, and dunning escalation. + +This selective composition is central to the project. + +## Primary Users + +The project is intended for: + +* developers building coordination applications +* product teams defining communication-heavy business processes +* operations teams monitoring unresolved coordination cases +* compliance and legal stakeholders reviewing evidence +* automation agents creating, inspecting, and updating cases +* integration partners connecting channel, identity, payment, signature, document, CRM, ERP, or messaging systems + +## Design Priorities + +The project should prioritize: + +1. **Result orientation** + Model success through intended result satisfaction, not message dispatch. + +2. **Headless operation** + Provide APIs and events rather than a fixed UI. + +3. **Evidence-driven state** + Preserve observations and derive assessments from evidence. + +4. **Uncertainty awareness** + Treat ambiguous states as first-class, especially where channel evidence is weak. + +5. **Adapter extensibility** + Support many protocols, systems, and providers through normalized actions and events. + +6. **Scenario modularity** + Allow applications to select the relevant subset of concepts. + +7. **Auditability** + Explain why a participant or case was marked successful, failed, partial, expired, escalated, or unresolved. + +8. **Agent-friendliness** + Provide clear schemas, APIs, events, and state models suitable for automation. + +## Initial Implementation Focus + +The first implementation should prove the core abstraction with a minimal but useful scenario. + +Recommended MVP focus: + +* create a coordination case +* add multiple participants +* register one payload +* define an intended result +* create participant-specific access grants +* record simulated or real notification events +* record access and interaction evidence +* derive participant-level state +* derive case-level progress +* generate follow-up actions from policy +* close the case based on sufficient evidence + +The first real adapters should likely be: + +* email adapter +* portal/action-surface adapter +* webhook adapter + +Simulated adapters are acceptable for proving the core runtime. + +## Non-Goals + +`coordination-engine` is not primarily: + +* an email sending system +* a newsletter platform +* a campaign marketing tool +* a document management system +* a payment processor +* a signature provider +* a CRM +* an ERP +* a full workflow/BPMN engine +* a legal proof system by default + +It may integrate with these systems, but its role is to coordinate result-oriented interaction across them. + +## Success Criteria + +The project is successful when a developer can define a scenario-specific headless coordination application by composing framework concepts and policies, without hardcoding all coordination logic from scratch. + +The first useful version should demonstrate: + +* multi-participant coordination +* evidence-driven state changes +* distinction between notification, delivery, access, and interaction +* ambiguous or unresolved states +* policy-based follow-up actions +* result-based closure +* traceability from final assessment to evidence events + +## Guiding Statement + +`coordination-engine` coordinates participants around payloads and action surfaces through notifications, deliveries, interactions, access control, evidence, and policies to achieve intended results under uncertainty. + diff --git a/history/260601-AdapterInterfaceSpecification.md b/history/260601-AdapterInterfaceSpecification.md new file mode 100644 index 0000000..1712075 --- /dev/null +++ b/history/260601-AdapterInterfaceSpecification.md @@ -0,0 +1,1342 @@ +# AdapterInterfaceSpecification.md + +## 1. Document Status + +**Document:** AdapterInterfaceSpecification.md +**Project:** coordination-engine +**Version:** 1.0 +**Status:** Release Candidate +**Scope:** Contract between coordination-engine and external adapters +**Primary Reference Adapter:** email-connect + +## 2. Purpose + +This document defines the adapter interface contract between `coordination-engine` and external adapter components such as `email-connect`, `rss-connect`, `sms-connect`, `push-connect`, `xmpp-connect`, `portal-connect`, `payment-connect`, `signature-connect`, `document-connect`, `identity-connect`, and future adapters. + +The adapter interface allows `coordination-engine` to remain protocol-neutral while integrating with concrete technologies, providers, systems, and communication channels. + +Adapters translate between external systems and the internal coordination model by: + +1. Declaring capabilities. +2. Accepting action requests. +3. Returning action results. +4. Emitting normalized evidence events. +5. Exposing health and operational state. +6. Preserving correlation between coordination cases and external provider events. + +The goal of this specification is to ensure that adapters are independently useful components while still being compatible with `coordination-engine`. + +## 3. Core Design Principle + +Adapters are not merely transport drivers. + +An adapter is a capability component that can perform actions and produce evidence. + +```text +Adapter = Capability Descriptor + Action Provider + Signal Provider +``` + +The coordination engine remains responsible for: + +* coordination case lifecycle +* result evaluation +* participant state derivation +* policy decisions +* follow-up decisions +* uncertainty handling +* case-level success/failure/expiry/escalation + +The adapter remains responsible for: + +* provider-specific integration +* protocol-specific action execution +* native event ingestion +* native-to-normalized event mapping +* adapter-level diagnostics +* provider-level correlation +* adapter health reporting + +## 4. Boundary Between coordination-engine and Adapters + +### 4.1 coordination-engine Owns + +`coordination-engine` owns: + +* `CoordinationCase` +* `Participant` +* `IntendedResult` +* case-level state +* participant-level state +* result predicates +* policy evaluation +* next-action selection +* evidence ledger +* adapter selection +* scenario-specific orchestration +* closure, failure, expiry, escalation, and manual override + +### 4.2 Adapters Own + +Adapters own: + +* provider communication +* protocol-specific execution +* provider credentials and configuration +* native event ingestion +* provider-native identifiers +* native status interpretation +* native-to-normalized event mapping +* adapter-specific retries where appropriate +* adapter-specific diagnostics +* adapter health + +### 4.3 Shared Contract + +The shared contract consists of: + +* `AdapterDescriptor` +* `AdapterCapability` +* `AdapterActionRequest` +* `AdapterActionResult` +* `AdapterEvent` +* `EvidenceEvent` +* `AdapterHealth` +* `CorrelationContext` +* `EvidenceGrade` +* `ErrorDescriptor` + +## 5. Adapter Types + +An adapter may belong to one or more adapter types. + +Recommended canonical adapter types: + +```text +notification +communication +access +delivery +interaction +identity +authority +payment +signature +document +archive +crm +erp +webhook +feed +system +manual +``` + +Examples: + +| Adapter repo | Likely adapter types | +| ------------------- | ---------------------------------------- | +| `email-connect` | notification, communication, interaction | +| `rss-connect` | notification, feed, communication | +| `sms-connect` | notification, communication | +| `push-connect` | notification, interaction | +| `portal-connect` | access, delivery, interaction, identity | +| `payment-connect` | payment, interaction, delivery | +| `signature-connect` | signature, identity, authority, delivery | +| `document-connect` | document, payload, archive, delivery | +| `identity-connect` | identity, authority | +| `webhook-connect` | system, delivery, interaction | + +Adapter type is descriptive, not exclusive. + +An adapter may expose capabilities from multiple types if that reflects its real function. + +## 6. Normative Language + +The following terms are used in this specification: + +* **MUST** means required for contract compliance. +* **SHOULD** means recommended unless a documented reason exists. +* **MAY** means optional. +* **MUST NOT** means prohibited for contract compliance. + +## 7. Core Interface Objects + +## 7.1 AdapterDescriptor + +An `AdapterDescriptor` declares the identity, version, capabilities, event support, evidence profile, and operational characteristics of an adapter. + +Adapters MUST expose a descriptor. + +```yaml +AdapterDescriptor: + adapter_id: string + adapter_name: string + adapter_version: string + adapter_contract_version: string + adapter_types: + - string + provider_family: string? + provider_name: string? + deployment_mode: embedded | sidecar | external | unknown + supported_actions: + - AdapterActionCapability + emitted_event_types: + - string + supported_channels: + - string + supported_endpoint_types: + - string + evidence_profile: AdapterEvidenceProfile + identity_profile: AdapterIdentityProfile + latency_profile: AdapterLatencyProfile? + cost_profile: AdapterCostProfile? + reliability_profile: AdapterReliabilityProfile? + configuration_schema_ref: string? + health_endpoint_ref: string? + documentation_ref: string? + limitations: + - string +``` + +### Required Fields + +The following fields are REQUIRED: + +```text +adapter_id +adapter_name +adapter_version +adapter_contract_version +adapter_types +supported_actions +emitted_event_types +supported_channels +supported_endpoint_types +evidence_profile +identity_profile +limitations +``` + +### Example: email-connect + +```yaml +adapter_id: email-connect.default +adapter_name: email-connect +adapter_version: 1.0.0 +adapter_contract_version: 1.0 +adapter_types: + - notification + - communication + - interaction +provider_family: email +provider_name: configurable +deployment_mode: external +supported_channels: + - email +supported_endpoint_types: + - email_address +supported_actions: + - action_type: notification.send + mode: async + idempotency_required: true + - action_type: notification.send_reminder + mode: async + idempotency_required: true + - action_type: recipient.suppress + mode: sync + idempotency_required: true + - action_type: recipient.unsuppress + mode: sync + idempotency_required: true +emitted_event_types: + - notification.attempt.accepted_by_provider + - notification.attempt.rejected_by_provider + - notification.endpoint.accepted + - notification.endpoint.deferred + - notification.endpoint.rejected_permanent + - notification.channel.complaint_received + - interaction.proxy_or_privacy_interaction + - interaction.scanner_or_bot_interaction + - interaction.unverified_actor_interaction + - interaction.reply_received +evidence_profile: + strongest_evidence_level: weak_to_medium + can_prove_human_awareness: false + can_prove_payload_delivery: false + can_prove_identity: false +identity_profile: + identity_strength: none + authority_strength: none +limitations: + - Recipient mail server acceptance does not prove inbox delivery. + - Open tracking is ambiguous and may be caused by privacy proxies. + - Click tracking may be caused by security scanners. + - Email events alone usually cannot prove intended-recipient awareness. +``` + +## 7.2 AdapterActionCapability + +An `AdapterActionCapability` declares one action an adapter can perform. + +```yaml +AdapterActionCapability: + action_type: string + description: string? + mode: sync | async | scheduled + idempotency_required: boolean + required_fields: + - string + optional_fields: + - string + supported_channels: + - string + supported_endpoint_types: + - string + expected_initial_events: + - string + possible_later_events: + - string + known_failure_modes: + - string +``` + +### Action Type Naming + +Action types SHOULD use dotted semantic names. + +Recommended families: + +```text +notification.* +access.* +delivery.* +interaction.* +identity.* +authority.* +payment.* +signature.* +document.* +archive.* +feed.* +webhook.* +recipient.* +system.* +``` + +Examples: + +```text +notification.send +notification.send_reminder +access.grant +access.revoke +delivery.publish_payload +delivery.request_submission +payment.create_request +payment.cancel_request +signature.create_envelope +signature.cancel_envelope +feed.publish_item +recipient.suppress +recipient.unsuppress +``` + +## 7.3 AdapterActionRequest + +An `AdapterActionRequest` is a command from `coordination-engine` to an adapter. + +Adapters MUST accept action requests in this conceptual shape, even if transport-specific serialization differs. + +```yaml +AdapterActionRequest: + request_id: string + action_type: string + coordination_case_id: string + participant_id: string? + payload_ref: ResourceRef? + action_surface_ref: ResourceRef? + channel: string? + target_endpoint: EndpointRef? + actor_context: ActorContext? + content: ActionContent? + template_ref: string? + variables: object? + tracking_context: TrackingContext + policy_context: PolicyContext? + security_context: SecurityContext? + idempotency_key: string + requested_at: timestamp + expires_at: timestamp? + metadata: object? +``` + +### Required Fields + +The following fields are REQUIRED: + +```text +request_id +action_type +coordination_case_id +tracking_context +idempotency_key +requested_at +``` + +Fields such as `participant_id`, `payload_ref`, `target_endpoint`, or `action_surface_ref` may be optional for system-level or broadcast actions, but adapters MAY require them for specific action types. + +### Idempotency + +`idempotency_key` MUST be provided for all externally visible actions. + +Adapters MUST treat repeated action requests with the same `idempotency_key` as duplicates of the same logical request. + +Adapters SHOULD return the original or current action result for duplicate requests. + +### Correlation + +`tracking_context` MUST contain enough information to correlate later adapter events back to the coordination case. + +```yaml +TrackingContext: + correlation_id: string + coordination_case_id: string + participant_id: string? + notification_id: string? + delivery_id: string? + interaction_id: string? + payload_id: string? + action_surface_id: string? + external_context: + - key: string + value: string +``` + +### EndpointRef + +```yaml +EndpointRef: + endpoint_id: string? + endpoint_type: string + value: string? + display_name: string? + verified: boolean? + metadata: object? +``` + +Examples of `endpoint_type`: + +```text +email_address +phone_number +push_token +xmpp_jid +rss_feed +webhook_url +portal_account +payment_account +signature_recipient +api_client +physical_address +``` + +### ActionContent + +`ActionContent` represents content or content references. + +Adapters SHOULD prefer references over large inline content where possible. + +```yaml +ActionContent: + content_ref: string? + subject: string? + body_text: string? + body_html: string? + payload_refs: + - ResourceRef + action_links: + - ActionLink + attachments: + - ResourceRef + inline_data: object? +``` + +### ResourceRef + +```yaml +ResourceRef: + ref_type: string + ref_id: string + uri: string? + version: string? + integrity_hash: string? + metadata: object? +``` + +### ActionLink + +```yaml +ActionLink: + link_id: string + purpose: string + url: string + expires_at: timestamp? + tokenized: boolean? + requires_authentication: boolean? +``` + +## 7.4 AdapterActionResult + +An `AdapterActionResult` is the adapter’s response to an action request. + +```yaml +AdapterActionResult: + request_id: string + adapter_id: string + accepted: boolean + action_state: AdapterActionState + adapter_operation_id: string? + provider_operation_id: string? + initial_events: + - EvidenceEvent + raw_response_ref: string? + error: ErrorDescriptor? + received_at: timestamp + completed_at: timestamp? + metadata: object? +``` + +### AdapterActionState + +```text +accepted +rejected +queued +scheduled +in_progress +completed +failed +cancelled +duplicate +unknown +``` + +### Important Rule + +An accepted action result is not proof that the intended result was achieved. + +Example: + +For email, `accepted: true` means the email adapter accepted the send request. It does not prove that the recipient received, saw, understood, or acted on the notification. + +## 7.5 AdapterEvent + +An `AdapterEvent` is an incoming event from an adapter before or during evidence normalization. + +Adapters MAY expose native-like adapter events, but MUST provide a mapping to `EvidenceEvent`. + +```yaml +AdapterEvent: + event_id: string + adapter_id: string + adapter_event_type: string + native_event_type: string? + occurred_at: timestamp + received_at: timestamp + correlation: CorrelationContext + participant_id: string? + payload_ref: ResourceRef? + action_surface_ref: ResourceRef? + endpoint_ref: EndpointRef? + actor_context: ActorContext? + provider_refs: + - ProviderRef + raw_event_ref: string? + normalization_status: normalized | ignored | failed | pending + normalized_events: + - EvidenceEvent + metadata: object? +``` + +## 7.6 EvidenceEvent + +An `EvidenceEvent` is the normalized event format consumed by the `evidence-ledger`. + +Adapters MUST produce or enable production of `EvidenceEvent` records. + +```yaml +EvidenceEvent: + evidence_event_id: string + event_type: string + event_family: string + occurred_at: timestamp + observed_at: timestamp + source_adapter_id: string? + source_adapter_event_id: string? + coordination_case_id: string + participant_id: string? + payload_ref: ResourceRef? + action_surface_ref: ResourceRef? + endpoint_ref: EndpointRef? + actor_context: ActorContext? + provider_refs: + - ProviderRef + normalized_meaning: string + evidence_grade: EvidenceGrade + confidence: ConfidenceGrade + raw_event_ref: string? + correlation: CorrelationContext + metadata: object? +``` + +### Required Fields + +The following fields are REQUIRED: + +```text +evidence_event_id +event_type +event_family +occurred_at +observed_at +coordination_case_id +normalized_meaning +evidence_grade +confidence +correlation +``` + +## 8. Canonical Event Families + +The following event families are canonical for adapter-generated evidence. + +```text +notification +access +delivery +interaction +identity +authority +payload +payment +signature +feed +webhook +system +manual +result +policy +``` + +Adapters SHOULD map native events into these families. + +## 9. Canonical Event Types + +This version defines a starter vocabulary. It is not exhaustive. + +## 9.1 Notification Events + +```text +notification.attempt.created +notification.attempt.accepted_by_adapter +notification.attempt.rejected_by_adapter +notification.attempt.accepted_by_provider +notification.attempt.rejected_by_provider +notification.attempt.queued +notification.attempt.scheduled +notification.attempt.delayed +notification.attempt.cancelled +notification.endpoint.accepted +notification.endpoint.deferred +notification.endpoint.rejected_temporary +notification.endpoint.rejected_permanent +notification.endpoint.unreachable +notification.endpoint.unknown +notification.channel.complaint_received +notification.channel.unsubscribe_received +notification.channel.suppression_added +notification.channel.suppression_removed +notification.channel.reputation_warning +``` + +## 9.2 Access Events + +```text +access.grant.created +access.grant.issued +access.grant.used +access.grant.denied +access.grant.expired +access.grant.revoked +access.grant.delegated +access.grant.suspicious_use_detected +``` + +## 9.3 Delivery Events + +```text +delivery.payload.available +delivery.payload.presented +delivery.payload.viewed +delivery.payload.retrieved +delivery.payload.downloaded +delivery.payload.submitted +delivery.payload.received +delivery.payload.validated +delivery.payload.validation_failed +delivery.payload.accepted +delivery.payload.rejected +delivery.payload.archived +delivery.payload.failed +``` + +## 9.4 Interaction Events + +```text +interaction.surface.opened +interaction.notification.opened +interaction.link.clicked +interaction.proxy_or_privacy_interaction +interaction.scanner_or_bot_interaction +interaction.unverified_actor_interaction +interaction.authenticated_actor_interaction +interaction.authorized_actor_interaction +interaction.reply_received +interaction.out_of_office_received +interaction.acknowledgement_recorded +interaction.decline_recorded +interaction.dispute_opened +``` + +## 9.5 Identity Events + +```text +identity.actor_unknown +identity.actor_suspected +identity.actor_authenticated +identity.actor_mfa_verified +identity.actor_verified_by_provider +identity.actor_mismatch_detected +``` + +## 9.6 Authority Events + +```text +authority.unknown +authority.self_asserted +authority.delegate_authorized +authority.organizational_representative_verified +authority.signer_authorized +authority.payment_authorized +authority.authorization_failed +``` + +## 9.7 Payment Events + +```text +payment.request.created +payment.request.presented +payment.started +payment.failed +payment.cancelled +payment.succeeded +payment.settled +payment.partially_settled +payment.refunded +payment.disputed +payment.promise_to_pay_recorded +``` + +## 9.8 Signature Events + +```text +signature.envelope.created +signature.envelope.sent +signature.document.viewed +signature.signing.started +signature.signing.completed +signature.signing.declined +signature.signing.expired +signature.signing.cancelled +signature.document.finalized +signature.document.archived +``` + +## 9.9 Feed/Webhook/System Events + +```text +feed.item.published +feed.item.updated +feed.item.removed +feed.item.fetched +webhook.request.sent +webhook.response.accepted +webhook.response.rejected +webhook.delivery.failed +system.adapter.health_changed +system.provider.degraded +system.provider.unavailable +``` + +## 10. EvidenceGrade + +An `EvidenceGrade` describes what an event can reasonably support. + +Adapters MUST assign an evidence grade to normalized evidence events. + +```yaml +EvidenceGrade: + strength: none | weak | medium | strong | conclusive | negative | ambiguous + actor_certainty: none | low | medium | high + authority_certainty: none | low | medium | high + payload_certainty: none | low | medium | high + interaction_certainty: none | low | medium | high + timing_certainty: none | low | medium | high + channel_certainty: none | low | medium | high + non_repudiation_strength: none | low | medium | high + notes: + - string +``` + +### Examples + +#### Email MX Acceptance + +```yaml +strength: weak +actor_certainty: none +authority_certainty: none +payload_certainty: low +interaction_certainty: none +timing_certainty: medium +channel_certainty: medium +non_repudiation_strength: none +notes: + - Recipient mail server acceptance does not prove inbox placement or human awareness. +``` + +#### Scanner-Like Email Click + +```yaml +strength: ambiguous +actor_certainty: low +authority_certainty: none +payload_certainty: low +interaction_certainty: low +timing_certainty: medium +channel_certainty: medium +non_repudiation_strength: none +notes: + - Click appears automated and should not be treated as recipient engagement. +``` + +#### Authenticated Portal Download + +```yaml +strength: strong +actor_certainty: high +authority_certainty: medium +payload_certainty: high +interaction_certainty: high +timing_certainty: high +channel_certainty: high +non_repudiation_strength: medium +notes: + - Authenticated participant retrieved the payload. +``` + +#### Qualified Signature + +```yaml +strength: conclusive +actor_certainty: high +authority_certainty: high +payload_certainty: high +interaction_certainty: high +timing_certainty: high +channel_certainty: high +non_repudiation_strength: high +notes: + - Signature provider reports completed signing with verified signer identity and document integrity. +``` + +## 11. ConfidenceGrade + +`ConfidenceGrade` expresses how confident the adapter is in the normalized interpretation. + +```yaml +ConfidenceGrade: + level: low | medium | high + basis: + - string +``` + +Example: + +```yaml +level: medium +basis: + - Provider event type is known. + - Native event contains provider message ID. + - Event was correlated to known action request. +``` + +## 12. ActorContext + +`ActorContext` describes the actor associated with an event, if known. + +```yaml +ActorContext: + actor_ref: string? + actor_type: human | organization | system | agent | bot | unknown + identity_strength: none | low | medium | high + authority_strength: none | low | medium | high + authenticated: boolean? + delegated: boolean? + provider_identity_ref: string? + metadata: object? +``` + +Adapters SHOULD avoid overclaiming identity. + +For example, `email-connect` SHOULD NOT mark a click as `authenticated` unless it is backed by a trusted identity or portal event. + +## 13. CorrelationContext + +Correlation is mandatory for useful adapter integration. + +```yaml +CorrelationContext: + correlation_id: string + request_id: string? + coordination_case_id: string + participant_id: string? + notification_id: string? + delivery_id: string? + interaction_id: string? + payload_id: string? + action_surface_id: string? + adapter_operation_id: string? + provider_operation_id: string? + provider_message_id: string? + external_refs: + - ProviderRef +``` + +Adapters MUST preserve correlation identifiers where technically possible. + +Adapters SHOULD include correlation identifiers in outbound provider metadata, message headers, webhook metadata, URLs, tokens, or provider custom arguments when supported. + +## 14. ProviderRef + +```yaml +ProviderRef: + provider_name: string + ref_type: string + ref_value: string +``` + +Examples: + +```yaml +provider_name: sendgrid +ref_type: message_id +ref_value: abc123 +``` + +```yaml +provider_name: stripe +ref_type: payment_intent_id +ref_value: pi_123 +``` + +## 15. ErrorDescriptor + +```yaml +ErrorDescriptor: + code: string + message: string + retryable: boolean + category: validation | authentication | authorization | configuration | provider | network | rate_limit | timeout | conflict | unknown + native_error_code: string? + native_error_message: string? + details: object? +``` + +Adapters MUST return an `ErrorDescriptor` when an action request is rejected or fails before acceptance. + +## 16. Adapter Health + +Adapters MUST expose health state. + +```yaml +AdapterHealth: + adapter_id: string + status: healthy | degraded | unavailable | misconfigured | unknown + checked_at: timestamp + provider_connectivity: healthy | degraded | unavailable | unknown + configuration_validity: valid | invalid | partial | unknown + credential_status: valid | expired | missing | insufficient | unknown + last_action_success_at: timestamp? + last_event_received_at: timestamp? + known_degradations: + - string + metrics: + actions_accepted_last_24h: integer? + actions_failed_last_24h: integer? + events_received_last_24h: integer? + average_action_latency_ms: integer? +``` + +## 17. Transport Patterns + +This specification does not mandate one technical transport. + +Adapters MAY integrate through: + +```text +REST API +event bus +message queue +webhook +gRPC +embedded library +CLI +file drop +polling +``` + +However, any transport MUST preserve the semantic contract defined here. + +## 18. Adapter Integration Modes + +## 18.1 Embedded Mode + +Adapter code runs in the same process as `coordination-engine`. + +Suitable for: + +* local development +* test adapters +* simulated adapters +* simple deployments + +## 18.2 Sidecar Mode + +Adapter runs next to the engine but is deployed separately. + +Suitable for: + +* credential isolation +* provider-specific webhook handling +* protocol-specific runtime dependencies + +## 18.3 External Service Mode + +Adapter is an independently deployed service. + +Suitable for: + +* reusable `*-connect` components +* multi-tenant installations +* independent scaling +* provider abstraction +* operational separation + +Preferred direction for production adapters: + +```text +*-connect repositories SHOULD implement external service mode first, +while optionally also offering embedded or sidecar modes. +``` + +## 19. Adapter Lifecycle + +Adapters SHOULD support the following lifecycle: + +```text +registered +configured +healthy +degraded +disabled +retired +``` + +### 19.1 Registration + +During registration, the adapter provides its `AdapterDescriptor`. + +### 19.2 Configuration + +Configuration may include credentials, provider selection, endpoint settings, webhook secrets, templates, routing policies, or tenant-specific settings. + +### 19.3 Health Monitoring + +The engine or platform periodically checks `AdapterHealth`. + +### 19.4 Action Dispatch + +The engine dispatches `AdapterActionRequest` objects to the adapter. + +### 19.5 Event Ingestion + +The adapter emits `AdapterEvent` or `EvidenceEvent` objects. + +### 19.6 Degradation Handling + +If an adapter is degraded, the policy engine may switch channel, delay actions, retry later, or escalate. + +## 20. Event Ordering, Idempotency, and Late Events + +### 20.1 Event Ordering + +Adapters MUST NOT assume events arrive in causal order. + +The engine MUST be able to process late events. + +Adapters SHOULD include accurate `occurred_at` and `observed_at` timestamps. + +### 20.2 Duplicate Events + +Adapters SHOULD provide stable event identifiers where possible. + +The engine MUST deduplicate events using: + +```text +evidence_event_id +adapter_event_id +provider event id +correlation context +event type +timestamp +``` + +### 20.3 Late Events + +Late events MUST still be accepted and recorded if they are relevant. + +A late hard bounce may change or annotate a previously unresolved participant state. + +A late payment settlement may close an otherwise escalated participant state. + +### 20.4 Idempotent Actions + +All externally visible actions MUST include `idempotency_key`. + +Adapters MUST avoid duplicate external side effects for repeated action requests with the same key. + +## 21. Security Requirements + +Adapters MUST: + +* protect provider credentials +* verify incoming provider webhooks where possible +* validate action request authenticity +* avoid leaking sensitive payloads in logs +* support least-privilege configuration +* preserve correlation without exposing unnecessary personal data +* avoid storing raw sensitive data unless explicitly configured + +Adapters SHOULD: + +* support tenant separation +* support secret rotation +* support signed callbacks or event signatures +* support audit logging of action execution + +## 22. Privacy Requirements + +Adapters SHOULD minimize personal data. + +Adapters SHOULD support: + +* endpoint references instead of full endpoint values where possible +* redaction of raw provider events +* retention limits for raw event data +* configurable storage of message content +* metadata-only mode +* data deletion or anonymization workflows where applicable + +The adapter interface MUST allow references to external payloads rather than requiring payload content to be copied into the adapter. + +## 23. Standalone Adapter Requirements + +Adapters are encouraged to be useful outside `coordination-engine`. + +A standalone adapter MAY provide: + +* provider abstraction +* operational dashboard +* message timeline API +* diagnostic API +* native event archive +* normalized event stream +* suppression management +* provider health checks +* local policy hints + +However, standalone features MUST NOT contradict the coordination-engine contract. + +For example, `email-connect` may provide a message-level status called `delivered_to_mx`, but it MUST NOT expose that as business-level proof of recipient awareness. + +## 24. email-connect Reference Mapping + +The following mapping defines expected behavior for the first reference adapter. + +| Email-native event | Normalized event | Evidence interpretation | +| --------------------------------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| Message accepted by email adapter | `notification.attempt.accepted_by_adapter` | Weak evidence that adapter accepted request | +| Provider accepted message | `notification.attempt.accepted_by_provider` | Weak evidence that sending began | +| Provider rejected message | `notification.attempt.rejected_by_provider` | Strong failure for this attempt | +| Recipient MX accepted message | `notification.endpoint.accepted` | Weak technical evidence, not awareness | +| Temporary deferral | `notification.endpoint.deferred` | Pending/uncertain | +| Soft bounce | `notification.endpoint.rejected_temporary` | Pending or retryable failure | +| Hard bounce | `notification.endpoint.rejected_permanent` | Strong endpoint failure | +| Spam complaint | `notification.channel.complaint_received` | Strong negative channel evidence | +| Unsubscribe/suppression | `notification.channel.unsubscribe_received` or `notification.channel.suppression_added` | Channel not viable or constrained | +| Open via privacy proxy | `interaction.proxy_or_privacy_interaction` | Ambiguous/weak | +| Human-like open | `interaction.notification.opened` | Weak-to-medium, not sufficient for high-assurance cases | +| Scanner-like click | `interaction.scanner_or_bot_interaction` | Ambiguous, should not count as recipient action | +| Unverified click | `interaction.unverified_actor_interaction` | Weak-to-medium, identity uncertain | +| Reply received | `interaction.reply_received` | Medium-to-strong awareness evidence, identity may still need validation | +| Out-of-office reply | `interaction.out_of_office_received` | Evidence mailbox exists, not completion | + +## 25. RSS Reference Mapping + +`rss-connect` should demonstrate pull-based notification semantics. + +| RSS-native event | Normalized event | Evidence interpretation | +| ---------------------- | ------------------------------------------ | ---------------------------------------- | +| Feed item published | `feed.item.published` | Payload/notice made available | +| Feed item updated | `feed.item.updated` | Published item changed | +| Feed fetched | `feed.item.fetched` | Weak evidence of reader/system retrieval | +| Link clicked from feed | `interaction.unverified_actor_interaction` | Weak-to-medium, identity uncertain | +| Feed item removed | `feed.item.removed` | Publication revoked or expired | + +RSS publication alone should usually not count as participant awareness unless scenario policy explicitly accepts publication as sufficient. + +## 26. Portal Reference Mapping + +`portal-connect` is expected to provide stronger access and interaction evidence. + +| Portal-native event | Normalized event | Evidence interpretation | +| ------------------------- | --------------------------------------------- | ----------------------------------- | +| Access grant issued | `access.grant.issued` | Access path created | +| Login successful | `identity.actor_authenticated` | Stronger actor evidence | +| Payload page opened | `interaction.authenticated_actor_interaction` | Actor reached action surface | +| Document viewed | `delivery.payload.viewed` | Strong payload interaction evidence | +| Document downloaded | `delivery.payload.downloaded` | Strong payload retrieval evidence | +| Acknowledgement submitted | `interaction.acknowledgement_recorded` | Strong result-relevant evidence | +| Access denied | `access.grant.denied` | Failed or blocked access attempt | +| Access expired | `access.grant.expired` | Access no longer usable | + +## 27. Compatibility Requirements for Adapter Repositories + +Every `*-connect` repository SHOULD contain: + +```text +INTENT.md +docs/AdapterImplementation.md +docs/EventMapping.md +docs/ProviderModel.md +docs/EvidenceClassification.md +schemas/ +src/ +tests/ +``` + +Every adapter repository MUST document: + +```text +Implemented adapter contract version +Supported actions +Emitted normalized events +Native event mappings +Evidence grading rules +Known limitations +Provider-specific assumptions +Security model +Privacy model +Standalone usage +coordination-engine integration mode +``` + +## 28. Versioning + +### 28.1 Contract Version + +Adapters MUST declare `adapter_contract_version`. + +This document defines: + +```text +adapter_contract_version: 1.0 +``` + +### 28.2 Backward Compatibility + +Minor extensions MAY add: + +* optional fields +* new event types +* new action types +* new adapter types +* new evidence metadata + +Breaking changes include: + +* removing required fields +* changing meaning of existing fields +* changing event semantics +* changing idempotency requirements +* changing evidence grade semantics + +Breaking changes require a new major version. + +## 29. Minimal Compliance Checklist + +An adapter is minimally compliant with AdapterInterfaceSpecification 1.0 if it: + +1. Exposes an `AdapterDescriptor`. +2. Declares supported actions. +3. Declares emitted event types. +4. Accepts `AdapterActionRequest` objects for supported actions. +5. Returns `AdapterActionResult` objects. +6. Supports idempotency keys. +7. Emits or maps to `EvidenceEvent` objects. +8. Provides `EvidenceGrade` and `ConfidenceGrade`. +9. Preserves `CorrelationContext`. +10. Exposes `AdapterHealth`. +11. Documents known limitations. +12. Does not overclaim result success. +13. Clearly separates provider-native events from normalized evidence. + +## 30. Non-Goals + +This adapter specification does not define: + +* the internal storage model of adapters +* the provider-specific API shape +* the transport protocol used between engine and adapter +* legal validity of evidence +* UI requirements +* billing models +* tenant management model +* full workflow semantics +* complete event vocabulary for every possible domain + +Those concerns may be defined in separate documents or scenario-specific specifications. + +## 31. Strategic Summary + +The adapter interface allows `coordination-engine` to remain a generalized coordination runtime while integrating with many concrete systems. + +Adapters perform actions and emit evidence. They translate external provider realities into a normalized coordination vocabulary without deciding overall business success. + +The central rule is: + +> Adapters report what happened and what it may mean. coordination-engine decides what that means for the coordination case. + +This distinction is essential for reliable, extensible, evidence-driven digital coordination. + diff --git a/history/260601-HybridmailAdapterSpecification.md b/history/260601-HybridmailAdapterSpecification.md new file mode 100644 index 0000000..be8ba24 --- /dev/null +++ b/history/260601-HybridmailAdapterSpecification.md @@ -0,0 +1,1787 @@ +# HybridmailAdapterSpecification.md + +## 1. Document Status + +**Document:** HybridmailAdapterSpecification.md +**Project:** hybridmail-connect +**Target Integration:** coordination-engine +**Adapter Contract:** AdapterInterfaceSpecification.md v1.0 +**Status:** Draft v1.0 +**Primary Scope:** Hybrid mail submission, production, postal handover, return-mail evidence, registered/delivery-confirmation variants, and vendor-specific hybrid-mail provider integration. + +## 2. Purpose + +This document specifies how `hybridmail-connect` models hybrid mail as a communication, delivery, and evidence channel and how it integrates with `coordination-engine`. + +Hybrid mail means that a digital payload, usually a PDF document, is submitted to a provider that performs physical production and postal dispatch. The provider may validate the document, print it, fold it, insert it into an envelope, frank it, hand it over to a postal carrier, and optionally provide tracking, return-mail information, registered-mail evidence, or delivery-confirmation evidence. + +Unlike email, SMS, RSS, or XMPP, hybrid mail has a physical production and delivery chain. The adapter therefore needs to model both digital submission and physical fulfillment. + +The goal of this specification is to define a provider-neutral hybrid-mail baseline that can be implemented in vendor-specific flavors for providers such as: + +* Deutsche Post / E-POSTBUSINESS API +* Pingen +* Binect +* future hybrid-mail providers + +The adapter MUST support a superset model with capability flags, because providers differ in document validation, upload flow, print options, postal products, return-mail processing, preview handling, status granularity, webhooks, registered-letter support, and international delivery. + +## 3. Core Principle + +Hybrid mail is a controlled physical-delivery channel initiated through digital submission. + +The adapter MUST distinguish: + +```text +digital submission +provider acceptance +document validation +production readiness +print production +enveloping +franking +handover to postal carrier +postal delivery evidence +registered or tracked delivery evidence +undeliverable / return-mail evidence +physical delivery uncertainty +coordination result evidence +``` + +Normal physical letter dispatch usually cannot prove that the intended recipient personally received, opened, read, understood, or acted on the payload. + +The adapter reports hybrid-mail evidence. `coordination-engine` decides whether that evidence satisfies the coordination case. + +## 4. Architectural Role + +### 4.1 Standalone Role + +As a standalone component, `hybridmail-connect` provides: + +* provider-neutral hybrid-mail submission +* PDF upload and document registration +* single-letter and bulk/serial-letter handling +* document validation +* address validation result normalization +* print and shipping option abstraction +* attachment handling +* preview retrieval where supported +* production status tracking +* postal handover status tracking +* return-mail/undeliverable processing +* registered-mail or delivery-confirmation tracking where supported +* vendor-specific provider abstraction +* hybrid-mail timeline and evidence assessment +* provider health and cutoff-time diagnostics +* normalized hybrid-mail evidence events + +### 4.2 coordination-engine Adapter Role + +As a `coordination-engine` adapter, `hybridmail-connect` provides: + +#### Actions + +* `delivery.submit_payload` +* `delivery.submit_letter` +* `delivery.submit_bulk` +* `delivery.validate_payload` +* `delivery.add_attachment` +* `delivery.set_options` +* `delivery.request_preview` +* `delivery.approve_for_sending` +* `delivery.cancel` +* `delivery.request_status` +* `delivery.request_return_info` +* `delivery.request_registered_tracking` +* `notification.register_tracking_context` + +#### Signals + +* `payload.validation_failed` +* `payload.validation_passed` +* `delivery.payload.submitted` +* `delivery.payload.accepted` +* `delivery.payload.rejected` +* `delivery.payload.available` +* `delivery.production.pending` +* `delivery.production.started` +* `delivery.production.completed` +* `delivery.postal.handed_over` +* `delivery.postal.in_transit` +* `delivery.postal.delivered` +* `delivery.postal.delivery_confirmed` +* `delivery.postal.undeliverable` +* `delivery.postal.return_received` +* `delivery.postal.forwarded` +* `delivery.postal.address_corrected` +* `delivery.postal.status_unknown` +* `delivery.postal.expired_without_final_status` +* `system.provider.degraded` +* `system.provider.unavailable` + +Some of these normalized events will not be available for all vendors. Provider flavors MUST declare which ones they support. + +## 5. Relationship to coordination-engine + +`hybridmail-connect` does not own: + +* `CoordinationCase` +* intended result evaluation +* participant-level success +* case-level success +* legal notice interpretation +* proof that the recipient opened or read the letter +* contract acceptance +* payment settlement +* signature completion +* follow-up and escalation policy + +`hybridmail-connect` owns: + +* hybrid-mail provider abstraction +* document submission +* provider identifiers +* document validation result mapping +* print/shipping option mapping +* status polling and webhook ingestion +* physical-production status normalization +* postal-handover status normalization +* return-mail / undeliverable status normalization +* registered-letter or delivery-confirmation evidence normalization +* hybrid-mail-native message timeline +* hybrid-mail evidence assessment + +The boundary rule is: + +> hybridmail-connect reports what happened in the hybrid-mail channel and what that may indicate. coordination-engine decides what that means for the coordination case. + +## 6. Hybrid Mail as Delivery Channel + +Within `coordination-engine`, hybrid mail is primarily a **delivery channel**, not just a notification channel. + +It is used when the communication payload itself is physically delivered as a paper letter. + +Examples: + +```text +PDF letter submitted for physical mailing +invoice printed and posted +contract notice sent by post +dunning letter sent as registered mail +mass mailing sent as bulk/serial letters +confidential employee payroll letter mailed physically +``` + +Hybrid mail may also produce notification-like evidence, because physical payload delivery is often treated as a strong delivery attempt. However, even physical delivery normally does not prove that the intended human personally read the letter. + +For high-assurance scenarios, hybrid-mail evidence may be combined with: + +* registered-mail evidence +* delivery confirmation +* return-mail processing +* manual evidence +* recipient response +* payment event +* signature event +* portal access +* acknowledgement + +## 7. Provider Landscape and Superset Baseline + +The public provider material suggests the following baseline capabilities. + +### 7.1 Deutsche Post / E-POSTBUSINESS API Baseline + +The Deutsche Post E-POSTBUSINESS API supports automated digital submission of PDF documents for physical mail shipment. It includes PDF upload, single and bulk processing, status requests, registered-letter use cases, German and international receivers, and requires attention to PDF/A-1b, address and layout requirements, metadata, and size constraints. It uses authenticated API access and rate limits. It provides process state and delivery/error information where available. + +### 7.2 Pingen Baseline + +Pingen supports a letter API with SDKs, OAuth-style client credential examples, file upload, letter creation, configurable address position, auto-send control, idempotency-key headers, rate limiting, configurable send limits, staging/sandbox, Track & Trace, webhooks, document validation, print and delivery options, return-mail processing, and delivery-confirmation-related webhook categories. + +Pingen explicitly distinguishes status tracking through handover and return-mail processing from ordinary post-office delivery confirmation. This is central for evidence grading. + +### 7.3 Binect Baseline + +Binect exposes a REST/Swagger API for digital letter dispatch with workflows such as document upload, attachment upload, shipping option configuration, preview retrieval, sending, and status queries. It validates uploaded documents for postal suitability, including address position, address format, and restricted areas. It supports single and serial/bulk letter concepts and provides shipping/production status information. + +### 7.4 Superset Design Implication + +The adapter MUST define a provider-neutral superset and require each provider flavor to expose a capability descriptor. + +Capabilities should be feature-gated rather than assumed. + +Examples: + +```text +supports_preview +supports_webhooks +supports_polling +supports_bulk_upload +supports_serial_letters +supports_attachments +supports_registered_mail +supports_delivery_confirmation +supports_return_mail +supports_address_correction +supports_international_mail +supports_pdfa_validation +supports_color_option +supports_duplex_option +supports_envelope_selection +supports_cancel_before_production +supports_staging_environment +``` + +## 8. Hybrid Mail Lifecycle Model + +`hybridmail-connect` models a hybrid-mail delivery as a lifecycle with observable phases. + +```text +letter.created +letter.payload_received +letter.rendered +letter.render_failed +letter.upload_requested +letter.accepted_by_adapter +letter.rejected_by_adapter +letter.uploaded_to_provider +letter.accepted_by_provider +letter.rejected_by_provider +letter.validation_started +letter.validation_passed +letter.validation_failed +letter.action_required +letter.preview_available +letter.options_set +letter.ready_for_sending +letter.submitted_for_sending +letter.cancelled_before_production +letter.processing_started +letter.transferred_for_printing +letter.printed +letter.enveloped +letter.franked +letter.handed_to_postal_service +letter.in_postal_delivery +letter.delivered_confirmed +letter.registered_delivery_event +letter.undeliverable_reported +letter.return_mail_received +letter.address_corrected +letter.forwarded +letter.finalized +letter.status_unknown +letter.expired_without_final_status +``` + +The lifecycle is provider-dependent. Not every provider exposes every state. + +The lifecycle is also not strictly linear. Late return-mail events may arrive after a letter was previously considered handed over or even after a case moved to another state. + +## 9. Letter, Document, Attempt, Batch, and Recipient Model + +The adapter MUST distinguish at least six layers. + +### 9.1 HybridmailLetter + +The logical letter delivery created by a client or coordination case. + +```yaml +HybridmailLetter: + hybridmail_letter_id: string + coordination_case_id: string? + participant_id: string? + purpose: string? + payload_ref: ResourceRef + document_ref: HybridmailDocumentRef? + recipient: PostalRecipient + sender: PostalSender? + options: HybridmailOptions? + tracking_context: TrackingContext + created_at: timestamp +``` + +### 9.2 HybridmailDocumentRef + +The submitted document or generated print artifact. + +```yaml +HybridmailDocumentRef: + document_id: string + provider_document_id: string? + document_type: pdf | pdfa | postscript | other + version: string? + page_count: integer? + sheet_count: integer? + color_pages: integer? + grayscale_pages: integer? + simplex_pages: integer? + duplex_pages: integer? + integrity_hash: string? + validation_state: unknown | pending | passed | action_required | failed + metadata: object? +``` + +### 9.3 HybridmailAttempt + +One submission attempt through one provider/configuration. + +```yaml +HybridmailAttempt: + hybridmail_attempt_id: string + hybridmail_letter_id: string + provider_name: string + provider_account_ref: string? + provider_document_id: string? + provider_letter_id: string? + provider_sending_id: string? + provider_tracking_id: string? + state: HybridmailAttemptState + created_at: timestamp + updated_at: timestamp +``` + +### 9.4 HybridmailBatch + +A batch or serial-letter group. + +```yaml +HybridmailBatch: + batch_id: string + provider_batch_id: string? + coordination_case_id: string? + batch_type: bulk | serial | campaign | mixed | unknown + letters: + - hybridmail_letter_id + state: HybridmailBatchState + created_at: timestamp + updated_at: timestamp +``` + +A batch can have partial success. Each child letter must retain its own evidence state. + +### 9.5 PostalRecipient + +The postal recipient extracted, provided, or inferred. + +```yaml +PostalRecipient: + recipient_id: string? + name_lines: + - string + organization: string? + street: string? + house_number: string? + address_addition: string? + postal_code: string? + city: string? + region: string? + country_code: string? + country_name: string? + address_source: extracted_from_document | provided_metadata | provider_detected | manual | unknown + address_quality: PostalAddressQuality? +``` + +### 9.6 PostalSender + +```yaml +PostalSender: + sender_id: string? + name_lines: + - string + organization: string? + return_address: PostalRecipient? + billing_account_ref: string? + sender_identity_ref: string? +``` + +## 10. Hybrid Mail Options Model + +The adapter MUST support a provider-neutral print and postal option model. + +```yaml +HybridmailOptions: + color_mode: color | grayscale | provider_default | unknown + print_sides: simplex | duplex | provider_default | unknown + envelope_type: c5 | c4 | window_left | window_right | no_window | provider_default | unknown + address_position: left | right | metadata | extracted | provider_default | unknown + postage_product: standard | priority | economy | registered | registered_return_receipt | delivery_confirmation | international | dialogpost | provider_specific + country_scope: domestic | international | mixed | unknown + dispatch_mode: manual_review | auto_send | submit_later | scheduled + desired_dispatch_date: date? + cutoff_policy_ref: string? + return_mail_handling: none | provider_processed | scan_return | digital_return_info | physical_return | unknown + address_correction_requested: boolean? + sustainability_option: none | carbon_reduced | provider_default | provider_specific + metadata: object? +``` + +Provider flavors MUST map their native options into this model and declare unsupported fields. + +## 11. Document Validation Model + +Document validation is central to hybrid mail. + +```yaml +HybridmailValidationResult: + document_id: string + validation_state: pending | passed | action_required | failed | unknown + issues: + - HybridmailValidationIssue + validated_at: timestamp? + provider_validation_ref: string? +``` + +```yaml +HybridmailValidationIssue: + issue_code: string + severity: info | warning | action_required | fatal + category: format | page_size | pdfa | address | restricted_area | margins | file_size | page_count | metadata | attachment | postage | country | provider_policy | unknown + message: string + page_number: integer? + bounding_box: object? + fixable: boolean? +``` + +Common validation categories: + +```text +invalid_pdf +invalid_pdfa +unsupported_file_type +not_a4_portrait +invalid_page_size +file_too_large +too_many_pages +address_not_detected +address_invalid +address_position_invalid +restricted_area_violation +barcode_area_violation +margin_violation +unsupported_country +unsupported_product +attachment_invalid +insufficient_credit +``` + +Validation failure usually maps to strong failure of the hybrid-mail attempt, but not necessarily failure of the coordination case if another channel or corrected document can be used. + +## 12. Preview Model + +Some providers support preview retrieval before dispatch. + +```yaml +HybridmailPreview: + preview_id: string + document_id: string + provider_preview_id: string? + preview_type: pdf | png | image | metadata | unknown + preview_uri: string? + generated_at: timestamp + expires_at: timestamp? + metadata: object? +``` + +Preview evidence maps to operational readiness, not physical delivery. + +## 13. Hybrid Mail Evidence Assessment + +`hybridmail-connect` should provide a hybrid-mail-native assessment separate from coordination-engine state. + +```yaml +HybridmailEvidenceAssessment: + hybridmail_letter_id: string + participant_id: string? + category: success | fail | undef + subclass: string + confidence: low | medium | high + strongest_signal: string? + evidence_summary: + - string + recommended_coordination_interpretation: string? +``` + +The assessment categories are adapter-level hints. + +### 13.1 Hybrid-Mail Adapter Success + +Hybrid-mail-level `success` means the channel produced strong hybrid-mail evidence. + +Possible subclasses: + +```text +success.provider_accepted +success.validation_passed +success.submitted_for_sending +success.production_started +success.transferred_for_printing +success.handed_to_postal_service +success.registered_delivery_event +success.delivery_confirmed +success.return_info_processed +``` + +Important: These are not automatically coordination success. + +For ordinary letters, `success.handed_to_postal_service` is strong dispatch evidence but not proof of physical delivery or human awareness. + +### 13.2 Hybrid-Mail Adapter Fail + +Hybrid-mail-level `fail` indicates strong evidence that the hybrid-mail attempt failed. + +Subclasses: + +```text +fail.missing_payload +fail.invalid_document +fail.validation_failed +fail.action_required_unresolved +fail.invalid_address +fail.unsupported_country +fail.unsupported_product +fail.insufficient_credit +fail.provider_rejected +fail.upload_failed +fail.production_failed +fail.cancelled +fail.postal_undeliverable +fail.return_mail_received +fail.expired_before_handover +``` + +### 13.3 Hybrid-Mail Adapter Undef + +Hybrid-mail-level `undef` is used when the adapter cannot determine physical delivery, recipient awareness, or final postal outcome. + +Subclasses: + +```text +undef.pending +undef.uploaded_only +undef.provider_accepted_only +undef.validation_pending +undef.ready_but_not_sent +undef.submitted_for_sending +undef.processing +undef.transferred_for_printing +undef.handed_to_postal_service_but_delivery_unproven +undef.postal_delivery_unknown +undef.registered_tracking_pending +undef.return_mail_pending +undef.no_final_status_expected +undef.conflicting_evidence +undef.channel_degraded +``` + +For ordinary physical letters, the adapter should expect `undef.handed_to_postal_service_but_delivery_unproven` to be common. + +## 14. Detailed Scenario Classification + +### 14.1 Pre-Submission Scenarios + +| Scenario | Hybridmail assessment | Normalized event | Notes | +| -------------------------------------- | --------------------------------------------- | --------------------------- | ----------------------------- | +| Missing PDF/payload | `fail.missing_payload` | `delivery.payload.failed` | No delivery possible | +| Unsupported document format | `fail.invalid_document` | `payload.validation_failed` | Provider may reject | +| Invalid PDF/PDF-A | `fail.validation_failed` | `payload.validation_failed` | Provider-specific | +| Non-A4 or unsupported page size | `fail.validation_failed` | `payload.validation_failed` | Common hybrid-mail constraint | +| Address not detected | `fail.invalid_address` or `action_required` | `payload.validation_failed` | May be fixable | +| Address in wrong window position | `fail.validation_failed` or `action_required` | `payload.validation_failed` | Provider-specific | +| Restricted area violation | `fail.validation_failed` or `action_required` | `payload.validation_failed` | Print/postal layout issue | +| Unsupported destination country | `fail.unsupported_country` | `delivery.payload.rejected` | Route unavailable | +| Unsupported postal product | `fail.unsupported_product` | `delivery.payload.rejected` | Option mismatch | +| Insufficient credit/account problem | `fail.insufficient_credit` | `delivery.payload.rejected` | Operational/account failure | +| Provider unavailable before acceptance | `undef.pending` or provider fail | action error | Retry-dependent | + +### 14.2 Upload and Validation Scenarios + +| Scenario | Hybridmail assessment | Normalized event | Notes | +| ------------------------ | ------------------------------------------ | ---------------------------------------------------- | --------------------------- | +| Adapter accepted request | `undef.pending` | `delivery.payload.submitted` | Adapter accepted work | +| Provider accepted upload | `undef.provider_accepted_only` | `delivery.payload.accepted` | Digital submission accepted | +| Provider rejected upload | `fail.provider_rejected` | `delivery.payload.rejected` | Strong attempt failure | +| Validation started | `undef.validation_pending` | `payload.validation_started` | Operational status | +| Validation passed | `success.validation_passed` | `payload.validation_passed` | Ready for options/send | +| Action required | `fail.action_required_unresolved` or undef | `payload.validation_failed` with severity | May be fixable | +| Validation failed fatal | `fail.validation_failed` | `payload.validation_failed` | Cannot send | +| Preview available | operational success | `delivery.payload.available` | Review evidence only | +| Attachment accepted | operational success | `delivery.payload.accepted` with attachment metadata | Not final delivery | +| Attachment failed | `fail.validation_failed` | `payload.validation_failed` | Must be corrected | + +### 14.3 Sending and Production Scenarios + +| Scenario | Hybridmail assessment | Normalized event | Notes | +| --------------------------- | ---------------------------------- | ------------------------------------------------ | --------------------------- | +| Options set | operational success | metadata event | Not delivery | +| Ready for sending | `undef.ready_but_not_sent` | `delivery.payload.available` | Awaiting dispatch approval | +| Submitted for sending | `success.submitted_for_sending` | `delivery.payload.submitted` | Provider instructed to send | +| Cancelled before production | `fail.cancelled` or cancelled | `delivery.payload.failed` or manual cancellation | Depends on policy | +| Processing started | `undef.processing` | `delivery.production.started` | Production chain began | +| Transferred for printing | `success.transferred_for_printing` | `delivery.production.started` | Strong production evidence | +| Printed | production success | `delivery.production.completed` | Not postal handover | +| Enveloped/franked | production success | `delivery.production.completed` | Not postal delivery | +| Production failed | `fail.production_failed` | `delivery.payload.failed` | Strong channel failure | +| Handover to postal service | `success.handed_to_postal_service` | `delivery.postal.handed_over` | Strong dispatch evidence | +| Same-day cutoff missed | `undef.processing` or delayed | metadata/channel warning | Adjust expected timing | + +### 14.4 Postal Delivery Scenarios + +| Scenario | Hybridmail assessment | Normalized event | Notes | +| ------------------------------ | ------------------------------------------------------ | ----------------------------------------- | ---------------------------------- | +| Ordinary letter handed over | `undef.handed_to_postal_service_but_delivery_unproven` | `delivery.postal.handed_over` | Strong dispatch, delivery unproven | +| Postal tracking ID received | `undef.postal_delivery_unknown` | `delivery.postal.in_transit` | Stronger traceability | +| Registered tracking event | `success.registered_delivery_event` | `delivery.postal.in_transit` or confirmed | Depends event | +| Delivery confirmed | `success.delivery_confirmed` | `delivery.postal.delivery_confirmed` | Strong physical delivery evidence | +| Return receipt received | `success.delivery_confirmed` | `delivery.postal.delivery_confirmed` | Stronger evidence | +| Undeliverable reported | `fail.postal_undeliverable` | `delivery.postal.undeliverable` | Strong negative evidence | +| Return mail received | `fail.return_mail_received` | `delivery.postal.return_received` | Strong negative evidence | +| Address corrected | operational/address evidence | `delivery.postal.address_corrected` | May support future correction | +| Forwarded due to moving | `undef.forwarded` or success-ish | `delivery.postal.forwarded` | Delivery path changed | +| No final status expected | `undef.no_final_status_expected` | metadata | Common for ordinary letters | +| No return after waiting period | still `undef` unless policy accepts | timeout metadata | Absence of return is not proof | + +### 14.5 Batch / Serial Letter Scenarios + +| Scenario | Hybridmail assessment | Normalized event | Notes | +| ------------------------------- | --------------------- | -------------------------------------- | ------------------------------- | +| Batch accepted | batch-level pending | `delivery.payload.submitted` | Child states needed | +| Some children validation-failed | partial | per-letter `payload.validation_failed` | Batch partial state | +| Some children ready | partial success | per-letter validation passed | Each letter tracked | +| Batch submitted | batch-level submitted | `delivery.payload.submitted` | Not each delivery | +| Some children handed over | partial dispatch | per-letter handover | Case aggregation needed | +| Some children undeliverable | partial negative | per-letter return/undeliverable | Do not fail whole batch blindly | + +## 15. Adapter-to-Coordination Mapping + +### 15.1 Core Mapping Table + +| Hybridmail-native event | Hybridmail assessment | coordination-engine event | Coordination interpretation | +| ------------------------ | ---------------------------------- | ------------------------------------------------ | ---------------------------- | +| Letter created | `undef.pending` | `delivery.payload.submitted` | Local delivery object exists | +| Provider upload accepted | `undef.provider_accepted_only` | `delivery.payload.accepted` | Digital submission accepted | +| Provider upload rejected | `fail.provider_rejected` | `delivery.payload.rejected` | Strong attempt failure | +| Validation passed | `success.validation_passed` | `payload.validation_passed` | Production-ready | +| Validation failed | `fail.validation_failed` | `payload.validation_failed` | Must correct or fallback | +| Preview available | operational success | `delivery.payload.available` | Review evidence | +| Ready for sending | `undef.ready_but_not_sent` | `delivery.payload.available` | Awaiting send | +| Submitted for sending | `success.submitted_for_sending` | `delivery.payload.submitted` | Provider instructed | +| Processing started | `undef.processing` | `delivery.production.started` | Production began | +| Transferred for printing | `success.transferred_for_printing` | `delivery.production.started` | Strong production evidence | +| Production completed | production success | `delivery.production.completed` | Printed/enveloped/franked | +| Handed to post | `success.handed_to_postal_service` | `delivery.postal.handed_over` | Strong dispatch evidence | +| Tracking ID received | stronger tracking evidence | `delivery.postal.in_transit` | Physical route traceability | +| Delivery confirmed | `success.delivery_confirmed` | `delivery.postal.delivery_confirmed` | Strong delivery evidence | +| Undeliverable | `fail.postal_undeliverable` | `delivery.postal.undeliverable` | Strong negative evidence | +| Return mail received | `fail.return_mail_received` | `delivery.postal.return_received` | Strong negative evidence | +| Address corrected | address signal | `delivery.postal.address_corrected` | Useful for contact registry | +| Cancelled | `fail.cancelled` | `delivery.payload.failed` or manual cancellation | Attempt stopped | + +### 15.2 Coordination Undef Subclasses + +`coordination-engine` may derive these uncertainty classes from hybrid-mail evidence: + +```text +undef.pending +undef.provider_accepted_only +undef.validation_pending +undef.ready_but_not_sent +undef.production_pending +undef.handed_to_postal_service_but_delivery_unproven +undef.postal_delivery_unknown +undef.no_final_status_expected +undef.return_mail_pending +undef.conflicting_evidence +undef.channel_degraded +undef.escalation_required +``` + +Hybrid mail commonly produces: + +```text +undef.handed_to_postal_service_but_delivery_unproven +undef.no_final_status_expected +undef.return_mail_pending +``` + +for ordinary letters. + +## 16. Evidence Grading Rules + +### 16.1 Provider Upload Accepted + +```yaml +event_type: delivery.payload.accepted +evidence_grade: + strength: weak + actor_certainty: none + authority_certainty: none + payload_certainty: medium + interaction_certainty: none + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: low +notes: + - Provider accepted the digital submission. + - Does not prove validation, production, postal handover, or delivery. +``` + +### 16.2 Validation Passed + +```yaml +event_type: payload.validation_passed +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Document passed provider validation for print/postal processing. + - Does not prove dispatch or delivery. +``` + +### 16.3 Validation Failed + +```yaml +event_type: payload.validation_failed +evidence_grade: + strength: negative + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Document cannot be processed until corrected or replaced. +``` + +### 16.4 Submitted for Sending + +```yaml +event_type: delivery.payload.submitted +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Letter was submitted into the provider sending process. + - Does not prove print production or postal handover. +``` + +### 16.5 Transferred for Printing + +```yaml +event_type: delivery.production.started +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Letter was transferred to or started in print production. + - Does not prove postal handover. +``` + +### 16.6 Handed to Postal Service + +```yaml +event_type: delivery.postal.handed_over +evidence_grade: + strength: strong + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - Letter was handed over to the postal delivery chain. + - Strong dispatch evidence. + - Does not prove recipient receipt or reading. +``` + +### 16.7 Delivery Confirmed / Registered Event + +```yaml +event_type: delivery.postal.delivery_confirmed +evidence_grade: + strength: strong + actor_certainty: low + authority_certainty: none + payload_certainty: high + interaction_certainty: low + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - Postal delivery confirmation or registered-mail event was received. + - This is strong physical-delivery evidence. + - It may still not prove that the intended human personally read the payload. +``` + +### 16.8 Return Mail / Undeliverable + +```yaml +event_type: delivery.postal.return_received +evidence_grade: + strength: negative + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - Letter was returned or reported undeliverable. + - Strong evidence that physical delivery failed for this attempt. +``` + +### 16.9 Address Correction + +```yaml +event_type: delivery.postal.address_corrected +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: medium + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Postal process or provider supplied address correction information. + - Useful for future delivery attempts and contact registry updates. +``` + +## 17. Status Timeline API + +`hybridmail-connect` SHOULD expose a delivery timeline suitable for standalone diagnostics and coordination audit. + +```yaml +HybridmailTimeline: + hybridmail_letter_id: string + provider_name: string + provider_document_id: string? + provider_letter_id: string? + provider_sending_id: string? + provider_tracking_id: string? + events: + - HybridmailTimelineEvent + current_assessment: HybridmailEvidenceAssessment +``` + +```yaml +HybridmailTimelineEvent: + event_id: string + event_type: string + occurred_at: timestamp + source: adapter | provider | print_center | postal_carrier | return_mail | operator + native_event_type: string? + normalized_event_type: string? + summary: string + evidence_grade: EvidenceGrade + raw_event_ref: string? +``` + +## 18. Adapter Descriptor + +`hybridmail-connect` MUST expose an `AdapterDescriptor` compatible with AdapterInterfaceSpecification.md v1.0. + +```yaml +adapter_id: hybridmail-connect.default +adapter_name: hybridmail-connect +adapter_version: 1.0.0 +adapter_contract_version: 1.0 +adapter_types: + - delivery + - communication + - document + - archive +provider_family: hybridmail +provider_name: configurable +deployment_mode: external +supported_channels: + - hybridmail + - postal_mail +supported_endpoint_types: + - postal_address +supported_actions: + - action_type: delivery.submit_letter + mode: async + idempotency_required: true + - action_type: delivery.submit_bulk + mode: async + idempotency_required: true + - action_type: delivery.validate_payload + mode: async + idempotency_required: true + - action_type: delivery.add_attachment + mode: async + idempotency_required: true + - action_type: delivery.set_options + mode: sync + idempotency_required: true + - action_type: delivery.request_preview + mode: async + idempotency_required: true + - action_type: delivery.approve_for_sending + mode: async + idempotency_required: true + - action_type: delivery.cancel + mode: async + idempotency_required: true + - action_type: delivery.request_status + mode: sync + idempotency_required: true +emitted_event_types: + - delivery.payload.submitted + - delivery.payload.accepted + - delivery.payload.rejected + - payload.validation_passed + - payload.validation_failed + - delivery.payload.available + - delivery.production.started + - delivery.production.completed + - delivery.postal.handed_over + - delivery.postal.in_transit + - delivery.postal.delivery_confirmed + - delivery.postal.undeliverable + - delivery.postal.return_received + - delivery.postal.forwarded + - delivery.postal.address_corrected + - delivery.postal.status_unknown + - system.provider.degraded + - system.provider.unavailable +evidence_profile: + strongest_evidence_level: strong + can_prove_human_awareness: false + can_prove_payload_delivery: partially + can_prove_identity: false +identity_profile: + identity_strength: none_to_low + authority_strength: none +limitations: + - Ordinary postal handover does not prove recipient receipt. + - Ordinary physical letters often do not provide final delivery confirmation. + - Delivery confirmation depends on postal product and provider support. + - Return-mail information may arrive late. + - Address extraction and validation differ by provider. + - Cancellation may only be possible before production starts. + - Batch-level status does not imply child-letter success. +``` + +## 19. Provider Flavor Descriptor + +Each vendor-specific implementation MUST expose a `HybridmailProviderFlavorDescriptor`. + +```yaml +HybridmailProviderFlavorDescriptor: + provider_name: dpag_epost | pingen | binect | other + provider_version: string? + supported_file_types: + - pdf + - pdfa + - postscript + supports_single_letter: boolean + supports_bulk: boolean + supports_serial_letters: boolean + supports_attachments: boolean + supports_preview: boolean + supports_webhooks: boolean + supports_polling: boolean + supports_registered_mail: boolean + supports_delivery_confirmation: boolean + supports_return_mail: boolean + supports_address_correction: boolean + supports_international_mail: boolean + supports_cancellation: boolean + supports_staging: boolean + supports_idempotency: boolean + supports_rate_limits: boolean + supported_print_options: + color_mode: boolean + simplex_duplex: boolean + envelope_selection: boolean + address_position: boolean + supported_postal_products: + - standard + - priority + - registered + - registered_return_receipt + - delivery_confirmation + - international + - dialogpost + - provider_specific + cutoff_times: + - country_scope: string + cutoff_local_time: string + notes: string? + limitations: + - string +``` + +## 20. Provider Flavor Baseline + +### 20.1 DPAG / E-POSTBUSINESS Flavor + +Likely capability baseline: + +```yaml +provider_name: dpag_epost +supports_single_letter: true +supports_bulk: true +supports_serial_letters: true +supports_attachments: provider_specific +supports_preview: provider_specific +supports_webhooks: provider_specific +supports_polling: true +supports_registered_mail: true +supports_delivery_confirmation: registered_product_dependent +supports_return_mail: provider_specific +supports_address_correction: provider_specific +supports_international_mail: true +supports_pdfa_validation: true +supports_staging: true +supports_idempotency: provider_specific +supported_file_types: + - pdf + - pdfa +``` + +Key flavor notes: + +```text +PDF/PDF-A handling is central. +PDF/A-1b knowledge is expected. +Address and layout requirements matter. +Status requests are core. +Registered-letter use cases are supported. +Bulk status information is relevant. +Activation, vendor/customer identifiers, and authenticated API access are central. +``` + +### 20.2 Pingen Flavor + +Likely capability baseline: + +```yaml +provider_name: pingen +supports_single_letter: true +supports_bulk: true +supports_serial_letters: true +supports_attachments: provider_specific +supports_preview: true +supports_webhooks: true +supports_polling: true +supports_registered_mail: true +supports_delivery_confirmation: product_dependent +supports_return_mail: true +supports_address_correction: provider_specific +supports_international_mail: true +supports_staging: true +supports_idempotency: true +supported_file_types: + - pdf +``` + +Key flavor notes: + +```text +File upload and letter creation are distinct concepts. +Address position can be configured. +Auto-send can be controlled. +Track & Trace provides detailed processing events. +Webhooks cover document issues, sent documents, undeliverable documents, and delivered sent documents. +Return mail processing is a first-class feature. +Normal postal delivery confirmation is not generally available for ordinary letters. +``` + +### 20.3 Binect Flavor + +Likely capability baseline: + +```yaml +provider_name: binect +supports_single_letter: true +supports_bulk: true +supports_serial_letters: true +supports_attachments: true +supports_preview: true +supports_webhooks: provider_specific +supports_polling: true +supports_registered_mail: provider_specific +supports_delivery_confirmation: provider_specific +supports_return_mail: provider_specific +supports_address_correction: provider_specific +supports_international_mail: provider_specific +supports_staging: provider_specific +supports_idempotency: provider_specific +supported_file_types: + - pdf + - postscript +``` + +Key flavor notes: + +```text +Document upload is a core workflow. +Attachments can be added. +Print and shipping options can be set. +PDF/PNG preview retrieval is supported. +Sending is performed separately after document preparation. +Status queries are supported. +Validation includes address position, address format, and restricted-area checks. +Default print/shipping options may be inherited and then overridden. +``` + +## 21. Action Request Handling + +### 21.1 `delivery.submit_letter` + +`delivery.submit_letter` submits a letter payload to a hybrid-mail provider. + +Required fields: + +```text +request_id +action_type +coordination_case_id +participant_id +payload_ref +target_endpoint +tracking_context +idempotency_key +requested_at +``` + +Example: + +```yaml +request_id: req_001 +action_type: delivery.submit_letter +coordination_case_id: case_123 +participant_id: participant_456 +channel: hybridmail +target_endpoint: + endpoint_type: postal_address + value: postal_recipient_ref_456 +payload_ref: + ref_type: coordination_payload + ref_id: payload_777 + uri: s3://bucket/document.pdf + integrity_hash: sha256:... +content: + payload_refs: + - ref_type: coordination_payload + ref_id: payload_777 +tracking_context: + correlation_id: corr_789 + coordination_case_id: case_123 + participant_id: participant_456 + delivery_id: delivery_001 + payload_id: payload_777 +idempotency_key: case_123:participant_456:hybridmail:delivery_001 +requested_at: 2026-01-01T12:00:00Z +metadata: + hybridmail_options: + color_mode: grayscale + print_sides: duplex + envelope_type: window_left + postage_product: standard +``` + +### 21.2 Action Result + +The adapter returns: + +```yaml +request_id: req_001 +adapter_id: hybridmail-connect.default +accepted: true +action_state: accepted +adapter_operation_id: hmop_001 +provider_operation_id: providerop_001 +initial_events: + - event_type: delivery.payload.submitted +received_at: 2026-01-01T12:00:01Z +``` + +This result does not prove validation, production, postal handover, or delivery. + +## 22. Provider Abstraction + +`hybridmail-connect` SHOULD support a provider abstraction layer. + +Provider integration responsibilities: + +* authenticate +* upload document +* create letter/document record +* add attachments +* configure options +* request preview +* submit for sending +* cancel where possible +* poll status +* ingest webhooks where supported +* normalize provider statuses +* parse validation issues +* parse return-mail information +* parse delivery-confirmation information +* expose provider health + +Provider model: + +```yaml +HybridmailProvider: + provider_name: string + provider_account_ref: string + supported_features: + - upload + - validation + - attachments + - options + - preview + - send + - cancel + - status_polling + - webhooks + - return_mail + - registered_mail + - delivery_confirmation + - bulk + - staging + event_mapping_ref: string + configuration_ref: string +``` + +Provider-specific modules should map to the hybrid-mail-native model first, then to normalized coordination events. + +```text +Provider event +→ hybridmail-native event +→ HybridmailEvidenceAssessment +→ EvidenceEvent for coordination-engine +``` + +## 23. Native Provider Event Mapping + +The adapter MUST support provider-specific mapping files or code modules. + +Common provider-native event groups: + +```text +uploaded +received +validated +validation_failed +ready +action_required +invalid +preview_available +submitted +processing +transferred_for_printing +printed +enveloped +franked +handed_to_post +sent +tracking_id_receivedered +delivery_confirmed +undeliverable +returned +address_corrected +forwarded +cancelled +failed +unknown +``` + +Important mapping rule: + +Provider terms such as `sent`, `delivered`, or `posted` MUST be mapped according to the provider’s actual semantics. + +For example: + +```text +"handed to post" → delivery.postal.handed_over +"delivery confirmed" → delivery.postal.delivery_confirmed +"undeliverable" → delivery.postal.undeliverable +``` + +A provider status called `delivered` MUST NOT automatically mean `coordinatit success` unless its semantics are documented and the coordination policy accepts it. + +## 24. Return Mail and Undeliverable Model + +```yaml +HybridmailReturnEvent: + return_event_id: string + hybridmail_letter_id: string + provider_return_id: string? + postal_tracking_id: string? + return_type: undeliverable | address_unknown | recipient_moved | refused | deceased | insufficient_address | mailbox_inaccessible | other | unknown + address_correction: PostalRecipient? + return_document_ref: ResourceRef? + occurred_at: timestamp? + observed_at: timestamp + raw_event_ref: string? +``` + +Return events usually create strong negative delivery evidence but may also provide address-correction information useful for future attempts. + +## 25. Registered / Delivery Confirmation Model + +```yaml +HybridmailDeliveryConfirmation: + confirmation_id: string + hybridmail_letter_id: string + provider_tracking_id: string? + postal_tracking_id: string? + product_type: registered | registered_return_receipt | delivery_confirmation | provider_specific + status: in_transit | attempted | delivered | refused | undeliverable | returned | unknown + recipient_signature_ref: ResourceRef? + confirmation_document_ref: ResourceRef? + occurred_at: timestamp? + observed_at: timestamp + raw_event_ref: string? +``` + +Delivery confirmation is stronger than ordinary handover evidence, but the exact legal and business meaning remains scenario-specific. + +## 26. Address Quality Model + +```yaml +PostalAddressQuality: + validation_state: unknown | valid | action_required | invalid | corrected + country_supported: boolean? + address_position_valid: boolean? + address_format_valid: boolean? + postal_code_valid: boolean? + street_known: boolean? + recipient_known: boolean? + correction_available: boolean? + correction_source: provider | postal_carrier | manual | unknown + issues: + - string +``` + +Address quality may be emitted as diagnostics and may update a contact registry, but it should not by itself create coordination result success. + +## 27. Channel Health + +`hybridmail-connect` SHOULD expose hybrid-mail channel health. + +```yaml +HybridmailChannelHealth: + provider_name: string + provider_account_ref: string? + status: healthy | degraded | failing | unknown + authentication_status: valid | expired | missing | insufficient | unknown + upload_status: healthy | degraded | unavailable | unknown + validation_status: healthy | degraded | unavailable | unknown + production_status: healthy | delayed | unavailable | unknown + postal_handover_status: healthy | delayed | unavailable | unknown + webhook_status: healthy | degraded | unavailable | not_supported | unknown + cutoff_status: before_cutoff | after_cutoff | no_production_day | unknown + known_degradations: + - string +``` + +Health-related normalized events: + +```text +system.provider.degraded +system.provider.unavailable +system.adapter.health_changed +``` + +## 28. Security Requirements + +`hybridmail-connect` MUST: + +* protect provider credentials +* protect uploaded payloads and document references +* support secure transport to providers +* preserve idempotency +* avoid duplicate physical sends for repeated idempotency keys +* validate provider webhooks where supported +* avoid logging sensitive document content +* protect postal recipient data +* support tenant/account separation where applicable +* support secure deletion or retention rules for raw payloads and previews +* clearly separate test/staging and production modes + +Duplicate-send prevention is especially critical because physical mail sends cost money and may have legal/business consequences. + +## 29. Privacy Requirements + +`hybridmail-connect` SHOULD: + +* store payload references instead of payload content where possible +* support metadata-only mode +* support masking of postal addresses +* support raw event redaction +* support configurable retention of documents, previews, and raw events +* avoid unnecessary storage of print payloads after submission +* separate operational diagnostics from coordination evidence +* support deletion or anonymization workflows where legally possible +* document provider-side retention limitations + +## 30. Reliability Requirements + +`hybridmail-connect` MUST support: + +* idempotent submit requests +* duplicate webhook event detection +* out-of-order event handling +* late return-mail events +* late delivery-confirmation events +* retryable upload failures +* non-retryable validation failures +* provider timeout handling +* provider polling fallback where webhooks are unavailable +* correlation preservation +* dead-letter handling for unprocessable events +* batch partial-failure handling + +Late events MUST be preserved. + +Example: + +```text +Letter handed to postal service on Monday. +Coordination case moves to waiting state. +Return mail event arrives two weeks later. +The return event must still be recorded and emitted as negative evidence. +``` + +## 31. Raw Event Preservation + +`hybridmail-connect` SHOULD preserve raw provider events or references to them. + +```yaml +RawHybridmailEventRef: + raw_event_id: string + provider_name: string + source: api_response | webhook | polling | print_center | postal_carrier | return_mail | operator + storage_ref: string? + received_at: timestamp + redacted: boolean +``` + +Normalized events should reference raw event data where available: + +```yaml +raw_event_ref: raw_hybridmail_event_123 +``` + +## 32. Minimal API Surface + +`hybridmail-connect` SHOULD expose a headless API. + +### 32.1 Adapter Contract API + +Required conceptual operations: + +```text +GET /adapter/descriptor +GET /adapter/health +POST /adapter/actions +POST /adapter/events/provider +GET /adapter/events +GET /adapter/letters/{id}/timeline +GET /adapter/letters/{id}/assessment +``` + +The actual transport may differ, but these conceptual operations should exist. + +### 32.2 Standalone API + +Useful standalone operations: + +```text +POST /hybridmail/letters +POST /hybridmail/batches +POST /hybridmail/letters/{id}/attachments +PUT /hybridmail/letters/{id}/options +POST /hybridmail/letters/{id}/preview +POST /hybridmail/letters/{id}/send +POST /hybridmail/letters/{id}/cancel +GET /hybridmail/letters/{id} +GET /hybridmail/letters/{id}/timeline +GET /hybridmail/letters/{id}/assessment +GET /hybridmail/letters/{id}/return-info +GET /hybridmail/letters/{id}/delivery-confirmation +GET /hybridmail/channel-health +``` + +## 33. Example End-to-End Flows + +### 33.1 Ordinary Physical Letter + +1. `coordination-engine` creates a coordination case. +2. A PDF payload is registered. +3. Policy selects hybrid mail as the delivery channel. +4. `coordination-engine` sends `delivery.submit_letter` to `hybridmail-connect`. +5. `hybridmail-connect` uploads the document to the provider. +6. Provider validates the document. +7. `hybridmail-connect` emits `payload.validation_passed`. +8. The letter is submitted for sending. +9. Provider transfers the letter for printing. +10. Print center hands the letter to the postal service. +11. `hybridmail-connect` emits `delivery.postal.handed_over`. +12. `coordination-engine` treats the participant as physically dispatched, but not necessarily as confirmed delivered unless policy accepts handover as sufficient. + +### 33.2 Validation Failure and Correction + +1. A PDF is uploaded. +2. Provider detects address-position or restricted-area issue. +3. `hybridmail-connect` emits `payload.validation_failed` with issue details. +4. `coordination-engine` marks the participant delivery attempt failed or action-required. +5. Policy requests correction, alternate document generation, or alternate channel. +6. Corrected payload is submitted with a new idempotency key or corrected version reference. + +### 33.3 Registered Letter + +1. A dunning letter is submitted with registered-mail option. +2. Provider validates and submits it. +3. Letter is handed to postal service. +4. Postal tracking events are received. +5. Delivery confirmation or return receipt event arrives. +6. `hybridmail-connect` emits `delivery.postal.delivery_confirmed`. +7. `coordination-engine` evaluates whether this satisfies the intended result. + +### 33.4 Return Mail + +1. Ordinary letter is handed to postal service. +2. No ordinary delivery confirmation is expected. +3. After several days or weeks, return mail is processed. +4. `hybridmail-connect` emits `delivery.postal.return_received`. +5. `coordination-engine` reopens, annotates, or fails the participant delivery state depending on policy. + +### 33.5 Bulk Mailing + +1. `coordination-engine` creates a batch delivery case. +2. `hybridmail-connect` submits a bulk/serial set of letters. +3. Some letters pass validation, others fail. +4. Per-letter evidence events are emitted. +5. `coordination-engine` aggregates participant states and avoids treating batch acceptance as universal success. + +## 34. Provider Implementation Guidance + +The first real provider flavor SHOULD be selected based on: + +* API documentation availability +* upload and validation quality +* status granularity +* webhook support +* preview support +* return-mail support +* registered-letter support +* staging/sandbox support +* idempotency or duplicate prevention support +* cost and operational availability +* alignment with the first coordination scenario + +The implementation SHOULD avoid hardcoding provider semantics into the core hybrid-mail model. + +Implementation mapping should follow: + +```text +Provider API/status/webhook +→ hybridmail-native event +→ HybridmailEvidenceAssessment +→ EvidenceEvent for coordination-engine +``` + +## 35. Message Stream / Product Separation + +`hybridmail-connect` SHOULD support separate configurations for different purposes. + +Recommended streams: + +```text +transactional_letters +legal_or_high_assurance_letters +registered_letters +invoices +dunning +payroll +marketing_mailings +test +``` + +Steparation may affect: + +* provider account +* sender identity +* billing account +* postal product +* print options +* return-mail handling +* retention policy +* production cutoff +* review/approval workflow +* evidence requirements + +## 36. Legal and Compliance Disclaimer + +`hybridmail-connect` does not by itself provide legal proof of notice, acceptance, signature, payment, or contract closure. + +It provides evidence from the hybrid-mail production and postal dispatch chain. + +Scenario-specific applications and `coordination-engine` policies may combine hybrid-mail evidence with registered-mail evidence, return-mail evidence, portal evidence, payment evidence, signature evidence, archive evidence, or manual evidence. + +The adapter MUST avoid naming technical postal events in ways that imply legal success unless the event type is explicitly scoped. + +Use: + +```text +delivery.postal.handed_over +delivery.postal.delivery_confirmed +delivery.postal.return_received +``` + +Avoid: + +```text +recipient_legally_notified +human_read_confirmed +contract_notice_completed +``` + +## 37. MVP Scope + +The first useful version of `hybridmail-connect` should implement: + +1. Adapter descriptor. +2. Provider flavor descriptor. +3. Adapter health endpoint. +4. `delivery.submit_letter`. +5. Idempotent submit request handling. +6. Simulated provider or one real provider flavor. +7. Letter, document, attempt, and recipient records. +8. Basic validation result mapping. +9. Basic print/shipping option mapping. +10. Basic status polling or webhook ingestion. +11. Evidence event generation. +12. Letter timeline. +13. Hybrid-mail evidence assessment. +14. Mapping to AdapterInterfaceSpecification.md v1.0. + +### MVP Required Hybrid-Mail Events + +```text +delivery.payload.submitted +delivery.payload.accepted +delivery.payload.rejected +payload.validation_passed +payload.validation_failed +delivery.payload.available +delivery.production.started +delivery.production.completed +delivery.postal.handed_over +delivery.postal.undeliverable +delivery.postal.return_received +delivery.postal.status_unknown +system.provider.degraded +system.provider.unavailable +``` + +### MVP Acceptance Criteria + +The MVP is acceptable when it can: + +1. Accept a coordination-compatible hybrid-mail submit request. +2. Dispatch or simulate a PDF letter submission. +3. Preserve correlation and idempotency. +4. Prevent duplicate physical sends for duplicate idempotency keys. +5. Record provider acceptance separately from validation. +6. Classify validation failure as strong negative payload evidence. +7. Classify postal handover as strong dispatch evidence but not proof of human awareness. +8. Classify ordinary no-final-status cases as unresolved rather than success. +9. Record return mail as strong negative evidence. +10. Provide a letter timeline. +11. Provide a hybrid-mail evidence assessment. +12. Integrate with coordination-engine without overclaiming success. + +## 38. Future Extensions + +Potential future capabilities: + +* multi-provider routing +* provider failover +* cost optimization by product/provider +* print-preview comparison +* PDF/A conversion support +* advanced address extraction +* address correction feedback loop +* return-mail scan ingestion +* registered-mail evidence archive +* delivery-confirmation document archive +* production SLA monitoring +* cutoff-aware scheduling +* country-specific postal product model +* automated correction workflows +* physical plus digital fallback patterns +* duplicate-content detection +* batch campaign analytics +* monthly journal / billing export integration +* print layout linting +* DIN 5008 address-window validation +* provider certification test suites + +## 39. Non-Goals + +`hybridmail-connect` is not: + +* a document authoring system +* a PDF renderer by default +* a postal carrier +* a legal notice system by itself +* a signature system +* a payment system +* a CRM +* a full workflow engine +* the owner of coordination case success + +It may integrate with such systems or be used by them. + +## 40. Summary + +`hybridmail-connect` models hybrid mail as a physical delivery channel initiated through digital submission. + +Its job is to: + +* submit PDF or print-ready documents +* validate hybrid-mail payloads +* map provider-specific print and shipping options +* track production and postal handover +* process return-mail and undeliverable evidence +* normalize registered or delivery-confirmation evidence +* expose document, letter, batch, and channel diagnostics +* integrate cleanly with `coordination-engine` + +The key rule is: + +> Hybrid-mail events are physical production and postal-chain evidence, not automatic coordination result satisfaction. hybridmail-connect reports hybrid-mail facts and uncertainty. coordination-engine evaluates intended results. + diff --git a/history/260601-RuntimeArchitectureAndAdapterSubsystem.md b/history/260601-RuntimeArchitectureAndAdapterSubsystem.md new file mode 100644 index 0000000..04c845e --- /dev/null +++ b/history/260601-RuntimeArchitectureAndAdapterSubsystem.md @@ -0,0 +1,1412 @@ +# Technical Specification Document: coordination-engine Architecture and Adapter Subsystem + +## 1. Document Status + +**Status:** Draft +**Project:** coordination-engine +**Document Type:** Technical Specification Document +**Scope:** Overall runtime architecture and adapter subsystem +**Primary Audience:** Developers, architects, integration partners, automation agents + +## 2. Purpose + +This document specifies the technical architecture of `coordination-engine` and describes how it uses adapters to integrate with external protocols, systems, providers, and technologies. + +`coordination-engine` is a generalized, headless runtime for digital coordination. It coordinates participants around payloads and action surfaces through notifications, deliveries, access control, interactions, evidence, and policies to achieve intended results under uncertainty. + +The adapter subsystem enables `coordination-engine` to remain protocol-neutral while integrating with concrete systems such as email, SMS, push notifications, RSS, XMPP, portals, payment providers, signature providers, document stores, identity providers, CRM systems, ERP systems, and webhooks. + +## 3. Architectural Intent + +The architecture is based on the following principles: + +1. **Coordination is result-driven.** + A coordination case succeeds when its intended result is satisfied, not when messages are merely sent. + +2. **Digital coordination is evidence-driven.** + The engine records observations from multiple systems and derives participant and case states from evidence. + +3. **Adapters are capability providers.** + Adapters are not simple transport drivers. They expose actions and emit normalized evidence. + +4. **External systems remain external.** + The engine should orchestrate and evaluate coordination, not replace email providers, portals, payment systems, signature systems, document stores, or identity systems. + +5. **Uncertainty is first-class.** + Many digital signals are ambiguous. The architecture must represent weak, conflicting, late, missing, or uncertain evidence. + +6. **Scenario-specific applications should compose concepts selectively.** + Simple use cases should not require payment, signature, or document-validation logic. Complex use cases should be able to add stronger identity, authority, payload, access, and evidence requirements. + +## 4. System Overview + +`coordination-engine` is a headless coordination runtime composed of a central coordination controller, specialized domain controllers, an evidence ledger, a policy engine, and an adapter layer. + +```text +coordination-engine + coordination-controller + result-controller + participant-controller + payload-controller + access-controller + notification-controller + delivery-controller + interaction-controller + identity-authority-controller + evidence-ledger + policy-engine + adapter-layer +``` + +The engine manages `CoordinationCase` objects. Each case represents a goal-directed process initiated by one party to achieve an intended result involving one or more participants. + +External systems connect through adapters. Adapters provide actions, such as sending an email or creating a payment request, and signals, such as delivery events, click events, payment settlement events, or signature completion events. + +## 5. Core Runtime Model + +### 5.1 CoordinationCase + +A `CoordinationCase` is the primary aggregate. + +It represents one goal-directed coordination process. + +Example cases: + +* deliver documents to recipients +* collect missing documents +* obtain AGB acceptance +* close contracts +* collect payments +* gather acknowledgements +* coordinate approvals + +Minimal structure: + +```yaml +CoordinationCase: + id: string + initiator: ActorRef + purpose: string + scenario_type: string + intended_result: IntendedResult + participants: ParticipantRef[] + payloads: PayloadRef[] + action_surfaces: ActionSurfaceRef[] + policies: PolicyRef[] + state: CoordinationCaseState + assessment: CoordinationAssessment + created_at: timestamp + updated_at: timestamp +``` + +### 5.2 Participant + +A `Participant` is an actor involved in a coordination case. + +Participants may be humans, organizations, systems, agents, delegates, signers, payers, approvers, respondents, recipients, or intermediaries. + +```yaml +Participant: + id: string + actor_ref: ActorRef + roles: string[] + contact_endpoints: EndpointRef[] + authority_profile_ref: string? + required_outcomes: RequiredOutcome[] + state: ParticipantState +``` + +### 5.3 IntendedResult + +An `IntendedResult` defines what the initiator wants to achieve. + +```yaml +IntendedResult: + id: string + result_type: string + target_population: ParticipantSelector + required_outcome: string + required_evidence_level: EvidenceLevel + threshold: Threshold? + deadline: timestamp? + partial_success_rules: RuleRef[] + failure_rules: RuleRef[] +``` + +Examples: + +```yaml +result_type: payload_access +required_outcome: pdf_downloaded +threshold: + type: percentage + value: 95 +deadline: 2026-12-31T23:59:59Z +``` + +```yaml +result_type: payment_collection +required_outcome: payment_settled +threshold: + type: amount + value: 100000 + currency: EUR +``` + +### 5.4 Payload + +A `Payload` is a meaningful resource involved in the case. + +Payloads are not limited to documents. They can include invoices, payment requests, contract drafts, signed contracts, submitted forms, data packages, receipts, terms versions, or structured messages. + +```yaml +Payload: + id: string + semantic_role: string + payload_type: string + version: string? + representation_refs: RepresentationRef[] + integrity_hash: string? + sensitivity: string? + retention_policy_ref: string? + validation_policy_ref: string? +``` + +### 5.5 ActionSurface + +An `ActionSurface` is the place where a participant can interact with a payload or perform a required action. + +Examples: + +* portal page +* mobile app screen +* payment page +* signature flow +* upload form +* approval screen +* chatbot flow +* API endpoint +* email reply parser +* XMPP bot + +```yaml +ActionSurface: + id: string + type: string + adapter_ref: string + url: string? + endpoint_ref: string? + supported_interactions: string[] + access_policy_ref: string? +``` + +### 5.6 EvidenceEvent + +An `EvidenceEvent` is a normalized observation used by the engine to derive state. + +```yaml +EvidenceEvent: + id: string + event_type: string + source_adapter: string? + native_event_type: string? + coordination_case_id: string + participant_id: string? + payload_id: string? + action_surface_id: string? + actor_ref: ActorRef? + timestamp: timestamp + normalized_meaning: string + confidence: ConfidenceGrade + evidence_grade: EvidenceGrade + raw_event_ref: string? + correlation: CorrelationContext +``` + +Evidence events are the basis for assessment, policy evaluation, auditability, and explainability. + +## 6. Runtime Components + +### 6.1 Coordination Controller + +The `coordination-controller` is the central runtime component. + +Responsibilities: + +* Create and manage `CoordinationCase` objects. +* Coordinate specialized controllers. +* Maintain case lifecycle state. +* Request policy evaluations. +* Apply derived state transitions. +* Trigger adapter actions through the adapter layer. +* Close, fail, expire, pause, or escalate cases. + +The coordination controller does not directly speak to email, SMS, payment, signature, portal, or other external systems. It uses adapters. + +### 6.2 Result Controller + +The `result-controller` evaluates whether an intended result has been satisfied. + +Responsibilities: + +* Evaluate case-level success predicates. +* Evaluate participant-level completion. +* Handle thresholds, deadlines, partial success, failure, expiry, and manual override. +* Produce result assessment events. + +Example derived states: + +```text +active +partially_completed +completed_successfully +completed_partially +failed +expired +manually_closed +``` + +### 6.3 Participant Controller + +The `participant-controller` manages participant state within a case. + +Responsibilities: + +* Maintain participant roles and required outcomes. +* Link participants to payloads, action surfaces, endpoints, and evidence. +* Derive participant progress states. +* Support delegates, organizations, systems, and agents. + +Example participant states: + +```text +not_started +notification_pending +notified +awareness_uncertain +access_granted +accessed +interaction_started +required_action_completed +failed +expired +escalated +completed +``` + +### 6.4 Payload Controller + +The `payload-controller` manages payload identity, versioning, representation, integrity, validation, and lifecycle references. + +Responsibilities: + +* Register payloads. +* Link payloads to participants and intended results. +* Track payload availability. +* Track inbound and outbound payload references. +* Integrate with document stores, archives, or payload providers through adapters. + +The payload controller does not need to store the payload itself. It may only store metadata and references. + +### 6.5 Access Controller + +The `access-controller` manages access to payloads and action surfaces. + +Responsibilities: + +* Create access grants. +* Revoke access. +* Track access usage, denial, expiry, delegation, and abuse. +* Coordinate with identity and authority systems. +* Emit access-related evidence events. + +Example access states: + +```text +not_granted +granted +used +denied +expired +revoked +delegated +suspicious +``` + +### 6.6 Notification Controller + +The `notification-controller` manages awareness-oriented signals. + +Responsibilities: + +* Create notification attempts. +* Select channels according to policy. +* Trigger notification adapters. +* Record notification evidence. +* Distinguish technical delivery from awareness evidence. +* Support reminders, retries, and escalation. + +Notification does not equal delivery. A notification may point to an action surface or payload but does not necessarily carry the payload. + +### 6.7 Delivery Controller + +The `delivery-controller` manages payload availability, retrieval, submission, transfer, consumption, and completion. + +Responsibilities: + +* Track outbound delivery evidence. +* Track inbound delivery evidence. +* Track payload submission and validation results. +* Interpret delivery-side events as possible evidence for notification or result success. + +Delivery is generalized as controlled payload access, transfer, retrieval, submission, or consumption. + +### 6.8 Interaction Controller + +The `interaction-controller` classifies meaningful participant actions. + +Responsibilities: + +* Record interaction events. +* Distinguish anonymous, suspected, authenticated, delegated, authorized, bot, scanner, and proxy interactions. +* Determine whether interactions are result-relevant. +* Feed interaction evidence into participant and result assessment. + +Example interactions: + +```text +notification_opened +link_clicked +portal_opened +document_viewed +pdf_downloaded +form_started +form_submitted +payment_started +payment_settled +contract_viewed +signature_completed +acknowledgement_recorded +``` + +### 6.9 Identity & Authority Controller + +The `identity-authority-controller` evaluates actor identity and authority. + +Responsibilities: + +* Link actors to participants. +* Evaluate authentication strength. +* Evaluate authorization, delegation, and representation. +* Provide evidence grades for identity and authority. +* Integrate with IAM, SSO, MFA, signature providers, or authorization systems. + +Example identity grades: + +```text +unknown +device_known +session_authenticated +mfa_verified +identity_provider_verified +qualified_identity_verified +``` + +Example authority grades: + +```text +unknown +self_authorized +delegated +organizational_representative +authorized_signer +payment_authorized +admin_override +``` + +### 6.10 Evidence Ledger + +The `evidence-ledger` stores normalized observations. + +Responsibilities: + +* Persist evidence events. +* Preserve raw event references. +* Support idempotency and deduplication. +* Maintain correlation across adapters. +* Support audit reconstruction. +* Support state derivation and explainability. + +The evidence ledger should be append-oriented. Derived state may be updated, but original evidence events should remain traceable. + +### 6.11 Policy Engine + +The `policy-engine` interprets state and evidence to determine next actions. + +Responsibilities: + +* Evaluate result policies. +* Evaluate follow-up policies. +* Evaluate escalation rules. +* Evaluate retry and timeout rules. +* Generate next-action recommendations or commands. +* Explain why actions were generated. + +Example next actions: + +```text +wait +send_notification +send_reminder +retry +switch_channel +escalate +request_correction +revoke_access +extend_deadline +manual_review +close_success +close_failure +expire_case +``` + +### 6.12 Adapter Layer + +The `adapter-layer` connects `coordination-engine` to external systems. + +Responsibilities: + +* Register adapter capabilities. +* Dispatch adapter actions. +* Receive adapter events. +* Normalize events into evidence. +* Monitor adapter health. +* Enforce idempotency and correlation. +* Isolate provider-specific logic from the core engine. + +Adapters may be implemented as separate services or libraries. The preferred architectural direction is separate `*-connect` repositories. + +Examples: + +```text +email-connect +rss-connect +sms-connect +push-connect +xmpp-connect +portal-connect +payment-connect +signature-connect +document-connect +identity-connect +crm-connect +erp-connect +``` + +## 7. Adapter Architecture + +### 7.1 Adapter Definition + +An adapter is a component that connects `coordination-engine` with an external protocol, provider, system, or technology. + +An adapter has three roles: + +```text +Adapter = Action Provider + Signal Provider + Capability Descriptor +``` + +### 7.2 Action Provider + +As an action provider, the adapter performs operations requested by `coordination-engine`. + +Examples: + +* send email +* send SMS +* publish RSS item +* create push notification +* grant portal access +* create payment request +* create signature envelope +* revoke access token +* archive document +* request identity verification + +### 7.3 Signal Provider + +As a signal provider, the adapter emits observations. + +Examples: + +* email provider accepted message +* email hard bounce received +* SMS delivery receipt received +* push notification tapped +* portal login completed +* document downloaded +* payment settled +* signature completed +* form submitted +* identity verified + +### 7.4 Capability Descriptor + +As a capability descriptor, the adapter declares what it can do and what evidence it can provide. + +This allows `coordination-engine` to reason about which adapters are suitable for a scenario. + +## 8. Adapter Contract + +### 8.1 AdapterDescriptor + +Each adapter shall expose an `AdapterDescriptor`. + +```yaml +AdapterDescriptor: + adapter_id: string + adapter_type: string + adapter_name: string + adapter_version: string + contract_version: string + supported_actions: + - AdapterActionCapability + emitted_event_types: + - string + supported_channels: + - string + evidence_strength_profile: EvidenceStrengthProfile + identity_strength_profile: IdentityStrengthProfile + latency_profile: LatencyProfile + cost_profile: CostProfile + failure_modes: + - string + configuration_schema_ref: string? + health_endpoint: string? +``` + +Example: + +```yaml +adapter_id: email-connect.default +adapter_type: email +adapter_name: email-connect +adapter_version: 0.1.0 +contract_version: 0.1.0 +supported_channels: + - email +supported_actions: + - action_type: notification.send + - action_type: notification.send_reminder +emitted_event_types: + - notification.attempt.accepted_by_provider + - notification.endpoint.accepted + - notification.endpoint.rejected_permanent + - interaction.proxy_or_privacy_interaction + - interaction.scanner_or_bot_interaction + - interaction.unverified_actor_interaction +failure_modes: + - provider_rejected + - endpoint_rejected + - deferred + - hard_bounce + - soft_bounce + - complaint + - suppressed +``` + +### 8.2 AdapterActionRequest + +An `AdapterActionRequest` is sent by the engine to an adapter. + +```yaml +AdapterActionRequest: + request_id: string + action_type: string + coordination_case_id: string + participant_id: string? + payload_ref: string? + action_surface_ref: string? + channel: string? + target_endpoint: EndpointRef? + content_ref: string? + template_ref: string? + variables: object? + tracking_context: TrackingContext + policy_context: PolicyContext? + idempotency_key: string + requested_at: timestamp +``` + +Example for email: + +```yaml +request_id: req_123 +action_type: notification.send +coordination_case_id: case_456 +participant_id: participant_789 +channel: email +target_endpoint: + type: email + value: recipient@example.com +template_ref: secure-document-available +variables: + recipient_name: Example Recipient + portal_link: https://portal.example.com/access/abc +tracking_context: + correlation_id: corr_001 + notification_id: notif_001 +idempotency_key: case_456:participant_789:notif_001 +``` + +### 8.3 AdapterActionResult + +An `AdapterActionResult` is returned by the adapter after receiving an action request. + +```yaml +AdapterActionResult: + request_id: string + adapter_id: string + accepted: boolean + adapter_operation_id: string? + provider_operation_id: string? + initial_status: string + raw_response_ref: string? + normalized_events: + - EvidenceEvent + error: + code: string? + message: string? + retryable: boolean? +``` + +The action result is not final proof of outcome. It only records whether the adapter accepted or rejected the requested action and may emit initial evidence events. + +### 8.4 AdapterEvent + +An `AdapterEvent` is an incoming event from an adapter. + +```yaml +AdapterEvent: + event_id: string + adapter_id: string + event_type: string + native_event_type: string? + timestamp: timestamp + coordination_case_id: string? + participant_id: string? + payload_id: string? + action_surface_id: string? + message_ref: string? + endpoint_ref: EndpointRef? + actor_ref: ActorRef? + raw_event_ref: string? + normalized_meaning: string + confidence: ConfidenceGrade + evidence_grade: EvidenceGrade + correlation: CorrelationContext +``` + +Adapter events are usually converted into `EvidenceEvent` records in the evidence ledger. + +### 8.5 AdapterHealth + +Adapters should expose health information. + +```yaml +AdapterHealth: + adapter_id: string + status: healthy | degraded | unavailable | misconfigured + last_action_success_at: timestamp? + last_event_received_at: timestamp? + provider_connectivity: string? + configuration_validity: string? + known_degradation: + - string +``` + +## 9. Event Normalization + +### 9.1 Native Events vs Normalized Events + +External systems use different event vocabularies. The adapter layer is responsible for normalizing them. + +Example: + +```text +SendGrid delivered +Mailgun delivered +SES Delivery +Postmark Delivery +``` + +These may all map to: + +```text +notification.endpoint.accepted +``` + +with meaning: + +```text +recipient email server accepted the message +``` + +They should not map directly to business-level success. + +### 9.2 Normalized Event Families + +The engine should define canonical event families. + +```text +coordination.* +participant.* +payload.* +access.* +notification.* +delivery.* +interaction.* +identity.* +authority.* +payment.* +signature.* +policy.* +result.* +system.* +manual.* +``` + +### 9.3 Example Email Normalization + +```text +email.provider.accepted +→ notification.attempt.accepted_by_provider + +email.transport.mx_accepted +→ notification.endpoint.accepted + +email.transport.hard_bounce +→ notification.endpoint.rejected_permanent + +email.transport.soft_bounce +→ notification.endpoint.deferred + +email.open.proxy_like +→ interaction.proxy_or_privacy_interaction + +email.click.scanner_like +→ interaction.scanner_or_bot_interaction + +email.click.unverified +→ interaction.unverified_actor_interaction + +email.complaint +→ notification.channel.complaint_received +``` + +### 9.4 Example Portal Normalization + +```text +portal.login_success +→ identity.actor_authenticated + +portal.document_viewed +→ delivery.payload_viewed + +portal.pdf_downloaded +→ delivery.payload_retrieved + +portal.acknowledgement_submitted +→ interaction.acknowledgement_recorded +``` + +### 9.5 Example Payment Normalization + +```text +payment.request_created +→ delivery.payment_request_available + +payment.link_clicked +→ interaction.payment_surface_opened + +payment.started +→ interaction.payment_started + +payment.succeeded +→ payment.succeeded + +payment.settled +→ result.payment_settled + +payment.failed +→ payment.failed + +payment.disputed +→ payment.disputed +``` + +## 10. Evidence Grading + +Not all evidence is equally strong. The architecture must avoid treating weak technical signals as business success. + +### 10.1 EvidenceGrade + +A generic evidence grade may contain: + +```yaml +EvidenceGrade: + category: weak | medium | strong | conclusive | ambiguous | negative + actor_certainty: none | low | medium | high + authority_certainty: none | low | medium | high + payload_certainty: none | low | medium | high + interaction_certainty: none | low | medium | high + timing_certainty: none | low | medium | high + non_repudiation_strength: none | low | medium | high +``` + +### 10.2 Examples + +Email MX acceptance: + +```yaml +category: weak +actor_certainty: none +payload_certainty: low +interaction_certainty: none +``` + +Anonymous link click: + +```yaml +category: ambiguous +actor_certainty: low +interaction_certainty: medium +``` + +Authenticated portal login: + +```yaml +category: strong +actor_certainty: high +interaction_certainty: medium +``` + +PDF downloaded by authenticated participant: + +```yaml +category: strong +actor_certainty: high +payload_certainty: high +interaction_certainty: high +``` + +Qualified signature completed: + +```yaml +category: conclusive +actor_certainty: high +authority_certainty: high +payload_certainty: high +interaction_certainty: high +non_repudiation_strength: high +``` + +## 11. Case State Derivation + +The engine derives state from evidence and policy. + +### 11.1 Participant Assessment + +A participant assessment may include: + +```yaml +ParticipantAssessment: + participant_id: string + state: string + notification_state: string? + access_state: string? + delivery_state: string? + interaction_state: string? + result_state: string? + evidence_level: EvidenceLevel + uncertainty_class: string? + risk_level: string? + next_actions: + - NextAction +``` + +Example uncertainty classes: + +```text +undef.pending +undef.technical_acceptance_only +undef.no_signal +undef.weak_positive +undef.identity_uncertain +undef.channel_suspicious +undef.conflicting_evidence +undef.delivery_pending +undef.escalation_required +``` + +### 11.2 Case Assessment + +A case assessment may include: + +```yaml +CoordinationAssessment: + case_id: string + state: string + result_satisfied: boolean + participants_total: integer + participants_completed: integer + participants_pending: integer + participants_failed: integer + participants_escalated: integer + progress_percentage: number + deadline_risk: string? + channel_risk: string? + compliance_risk: string? + next_actions: + - NextAction +``` + +## 12. Policy Engine + +### 12.1 Policy Inputs + +The policy engine evaluates: + +* case state +* participant states +* evidence events +* evidence grades +* deadlines +* result predicates +* adapter health +* channel availability +* failure modes +* scenario-specific configuration + +### 12.2 Policy Outputs + +The policy engine produces: + +```yaml +NextAction: + id: string + action_type: string + target: string + priority: string + due_at: timestamp? + adapter_action_request: AdapterActionRequest? + explanation: string +``` + +Examples: + +```text +send_email_notification +send_sms_reminder +switch_to_push +grant_portal_access +request_manual_review +close_participant_success +close_case_partial +expire_case +``` + +### 12.3 Policy Example + +```yaml +if: + participant.notification_state: notification.endpoint.accepted + participant.delivery_state: none + age_since_notification: ">72h" +then: + action_type: send_reminder + channel_preference: sms + explanation: "Email was technically accepted but no access or interaction evidence was observed within 72 hours." +``` + +## 13. Adapter Integration Modes + +### 13.1 Embedded Library Mode + +An adapter may be included as a library in the same deployment. + +Suitable for: + +* simple simulations +* testing +* local development +* low-volume integrations + +### 13.2 Sidecar Service Mode + +An adapter may run as a service alongside the engine. + +Suitable for: + +* provider credentials isolation +* webhook ingestion +* retry handling +* operational separation + +### 13.3 External Service Mode + +An adapter may run as an independently deployed service. + +Suitable for: + +* reusable connectors like `email-connect` +* multi-tenant environments +* shared provider integrations +* separate scaling and security boundaries + +Preferred direction: + +```text +coordination-engine owns the contract. +*-connect repos implement the contract. +``` + +## 14. Adapter Repository Pattern + +Adapters should preferably live in separate repositories. + +Example: + +```text +email-connect +rss-connect +sms-connect +push-connect +portal-connect +payment-connect +signature-connect +document-connect +identity-connect +``` + +Each adapter repository should provide: + +```text +INTENT.md +docs/AdapterArchitecture.md +docs/EventMapping.md +docs/ProviderModel.md +docs/EvidenceClassification.md +schemas/ +src/ +tests/ +``` + +Each adapter should include a compatibility declaration: + +```yaml +coordination_engine_compatibility: + adapter_contract_version: 0.1.0 + supported_actions: + - notification.send + emitted_events: + - notification.attempt.accepted_by_provider + - notification.endpoint.accepted + known_limitations: + - does_not_prove_inbox_delivery + - open_tracking_is_ambiguous +``` + +## 15. email-connect as Reference Adapter + +`email-connect` should be the first reference adapter. + +### 15.1 Standalone Role + +`email-connect` should be useful independently as a headless email communication and evidence service. + +Standalone capabilities: + +* send transactional emails +* ingest provider events +* track message timelines +* classify bounces +* handle suppression +* classify opens and clicks +* expose normalized email evidence +* debug delivery uncertainty + +### 15.2 coordination-engine Role + +As an adapter, `email-connect` provides: + +Actions: + +```text +notification.send +notification.send_reminder +notification.register_tracking_context +recipient.suppress +recipient.unsuppress +``` + +Signals: + +```text +notification.attempt.accepted_by_provider +notification.attempt.rejected_by_provider +notification.endpoint.accepted +notification.endpoint.deferred +notification.endpoint.rejected_permanent +interaction.proxy_or_privacy_interaction +interaction.scanner_or_bot_interaction +interaction.unverified_actor_interaction +notification.channel.complaint_received +notification.channel.unsubscribe_received +interaction.reply_received +``` + +### 15.3 Email-Specific Boundary + +`email-connect` may classify email events, but it does not decide case-level success. + +It may say: + +```text +MX accepted +hard bounce received +open appears privacy-proxy-like +click appears scanner-like +``` + +`coordination-engine` decides: + +```text +participant remains unresolved +send reminder +switch channel +close success +escalate +fail participant +``` + +## 16. Data Flow Examples + +### 16.1 Notification and Access Flow + +```text +1. Client creates CoordinationCase. +2. Client adds participants and payload. +3. Access Controller creates access grants. +4. Policy Engine requests notification. +5. Notification Controller creates AdapterActionRequest. +6. email-connect sends email. +7. email-connect emits provider accepted event. +8. Evidence Ledger records normalized event. +9. Later, portal-connect emits authenticated download event. +10. Evidence Ledger records delivery evidence. +11. Result Controller marks participant complete. +12. Coordination Controller updates case progress. +``` + +### 16.2 Payment Collection Flow + +```text +1. Client creates payment collection case. +2. Payload Controller registers invoices. +3. payment-connect creates payment requests. +4. email-connect sends payment notifications. +5. payment-connect emits payment_started and payment_settled events. +6. Evidence Ledger records events. +7. Result Controller marks paid participants complete. +8. Policy Engine triggers reminders for unpaid participants. +``` + +### 16.3 Contract Signing Flow + +```text +1. Client creates contract closure case. +2. Payload Controller registers contract draft. +3. Access Controller grants contract access. +4. signature-connect creates signature flow. +5. email-connect sends notification. +6. signature-connect emits viewed, signed, declined, expired events. +7. Identity & Authority Controller evaluates signer authority. +8. Result Controller closes participant on valid signature. +9. Coordination Controller closes case when required signatures are complete. +``` + +## 17. Reliability Requirements + +The architecture shall support: + +* idempotent action requests +* duplicate event detection +* late event handling +* retryable and non-retryable errors +* provider outages +* adapter degradation +* correlation across systems +* partial failure +* conflicting evidence +* manual correction or override + +## 18. Security Requirements + +The architecture shall support: + +* adapter credential isolation +* least-privilege adapter access +* secure webhook verification +* event authenticity validation where possible +* sensitive metadata minimization +* secure access grant handling +* audit logs for policy-driven and manual actions +* separation between payload storage and coordination metadata + +## 19. Privacy Requirements + +The engine should avoid storing unnecessary personal data. + +Recommended practices: + +* store participant references instead of full profiles where possible +* store payload references instead of payload content +* support retention policies +* support evidence minimization +* support redaction of raw event data +* separate audit requirements from operational tracking requirements + +## 20. Observability Requirements + +The system should expose metrics for: + +* active cases +* completed cases +* failed cases +* expired cases +* unresolved participants +* generated next actions +* adapter action success/failure +* adapter event ingestion latency +* evidence event volume +* policy evaluation results +* channel-level failure rates +* case-level progress +* deadline risk + +## 21. MVP Architecture + +The MVP should implement a minimal but coherent version of the architecture. + +### 21.1 MVP Components + +Required: + +* Coordination Controller +* Result Controller +* Participant Controller +* Payload Controller +* Access Controller +* Notification Controller +* Interaction Controller +* Evidence Ledger +* Policy Engine +* Adapter Contract +* Simulated Adapter + +Recommended: + +* email-connect reference adapter +* portal/action-surface simulated adapter +* webhook event ingestion + +### 21.2 MVP Scenario + +The recommended MVP scenario is **Digital Payload Notification and Access**. + +Flow: + +```text +create case +add participants +register payload +grant access +send notification +record provider acceptance +record ambiguous/no signal cases +record authenticated payload access +derive participant states +generate reminders +close completed participants +close case when result predicate is satisfied +``` + +### 21.3 MVP Acceptance Criteria + +The MVP shall demonstrate: + +1. Creation of a multi-participant coordination case. +2. Registration of one outbound payload. +3. Definition of an intended result. +4. Dispatch of notification actions through an adapter. +5. Recording of normalized evidence events. +6. Participant-level state derivation. +7. Case-level progress assessment. +8. Ambiguous evidence handling. +9. Policy-generated reminder or escalation. +10. Closure based on sufficient delivery or interaction evidence. +11. Traceability from final state to evidence events. + +## 22. Open Technical Questions + +1. Should policy rules be expressed as JSON/YAML, a DSL, embedded code, or an external rule engine? +2. Should the evidence ledger be strictly append-only from the first implementation? +3. Should adapter events be pushed, pulled, or both? +4. Should adapters call the engine directly or publish to an event bus? +5. How should raw event payloads be stored, redacted, and retained? +6. Should the engine own scheduling, or should it emit next actions to an external scheduler? +7. Should result predicates be evaluated synchronously on every event or asynchronously by a worker? +8. What is the minimal adapter contract version needed for `email-connect`? +9. Which provider should be used first for a real email adapter implementation? +10. How should manual override events be represented and audited? + +## 23. Non-Goals + +The architecture does not attempt to make `coordination-engine`: + +* a general-purpose BPMN engine +* an email provider +* a payment provider +* a signature provider +* a document store +* a CRM +* an ERP +* a legal proof system by default +* a complete UI application + +The engine coordinates across such systems through adapters. + +## 24. Strategic Implementation Guidance + +The recommended implementation path is: + +1. Define the core schemas. +2. Implement evidence ledger and case state derivation. +3. Implement adapter contract. +4. Implement simulated adapter. +5. Implement minimal notification-and-access scenario. +6. Create `email-connect` as reference adapter. +7. Add portal/action-surface adapter or simulation. +8. Validate policy-driven follow-up. +9. Extend toward request/collect, acceptance, signature, and payment patterns. + +## 25. Summary + +`coordination-engine` provides a digital-first coordination runtime. It manages goal-directed coordination cases by combining participants, payloads, action surfaces, access, notifications, deliveries, interactions, evidence, and policies. + +Adapters are the bridge between the engine and the outside world. They provide actions and signals, normalize provider-specific events, and declare their capabilities. The engine remains responsible for result evaluation, uncertainty handling, follow-up decisions, and coordination state. + +The core architectural idea is: + +> Adapters translate external capabilities and observations into coordination actions and evidence events. coordination-engine uses those events to evaluate intended results and control follow-up under uncertainty. + diff --git a/spec/AdapterInterfaceSpecification.md b/spec/AdapterInterfaceSpecification.md new file mode 100644 index 0000000..7c9599f --- /dev/null +++ b/spec/AdapterInterfaceSpecification.md @@ -0,0 +1,1991 @@ +# AdapterInterfaceSpecification.md + +## 1. Document Status + +**Document:** AdapterInterfaceSpecification.md +**Project:** coordination-engine +**Version:** 1.1 +**Status:** Updated Draft +**Scope:** Contract between coordination-engine and external adapters +**Primary Reference Adapters:** email-connect, sms-connect, rss-connect, xmpp-connect, hybridmail-connect + +## 2. Version 1.1 Change Summary + +Version 1.1 extends the original adapter interface based on concrete adapter design work for: + +* Email +* SMS +* RSS / Atom / WebSub +* XMPP +* Hybrid Mail + +The main changes are: + +1. Added `AdapterCapabilityProfile`. +2. Added `EvidenceCeiling`. +3. Added `NativeStatusMapping` / `NativeEventSemantics`. +4. Added universal weakest-safe-mapping rule. +5. Added `AdapterEvidenceAssessment`. +6. Added `EndpointQuality`. +7. Added `LateEventPolicy`. +8. Added `EventObservation`. +9. Added `FeatureDependency`. +10. Added `AssuranceCapability`. +11. Added adapter conformance levels. +12. Added golden test requirements. +13. Strengthened semantic underclaiming requirements. +14. Clarified that adapters provide evidence, not coordination-result truth. + +## 3. Purpose + +This document defines the adapter interface contract between `coordination-engine` and external adapter components such as: + +* `email-connect` +* `sms-connect` +* `rss-connect` +* `xmpp-connect` +* `hybridmail-connect` +* `push-connect` +* `portal-connect` +* `payment-connect` +* `signature-connect` +* `document-connect` +* `identity-connect` +* future adapters + +The adapter interface allows `coordination-engine` to remain protocol-neutral while integrating with concrete technologies, providers, systems, and communication channels. + +Adapters translate between external systems and the internal coordination model by: + +1. Declaring capabilities. +2. Declaring evidence ceilings. +3. Accepting action requests. +4. Returning action results. +5. Preserving native events. +6. Mapping native events conservatively. +7. Emitting normalized evidence events. +8. Providing advisory evidence assessments. +9. Exposing endpoint quality and adapter health. +10. Preserving correlation between coordination cases and external provider events. + +The goal of this specification is to ensure that adapters are independently useful components while still being compatible with `coordination-engine`. + +## 4. Core Design Principle + +Adapters are not merely transport drivers. + +An adapter is a capability component that can perform actions, observe signals, preserve native events, classify evidence, and expose its own limitations. + +```text +Adapter = + Capability Descriptor + + Action Provider + + Signal Provider + + Evidence Normalizer + + Evidence Ceiling Declaration + + Health / Diagnostics Provider +``` + +The coordination engine remains responsible for: + +* coordination case lifecycle +* result evaluation +* participant state derivation +* policy decisions +* follow-up decisions +* uncertainty handling +* case-level success/failure/expiry/escalation +* final interpretation of adapter evidence + +The adapter remains responsible for: + +* provider-specific integration +* protocol-specific action execution +* native event ingestion +* native event preservation +* native-to-normalized event mapping +* adapter-level evidence assessment +* adapter-specific diagnostics +* provider-level correlation +* adapter health reporting +* explicit limitation reporting + +## 5. Semantic Underclaiming Rule + +Adapters MUST underclaim rather than overclaim. + +If a native event or provider status is ambiguous, the adapter MUST map it to the weakest semantically safe normalized event and preserve the raw native event or status in metadata. + +Examples: + +```text +Email provider "delivered" +→ notification.endpoint.accepted +→ not recipient awareness + +SMS provider "delivered" +→ notification.endpoint.accepted with delivery-receipt metadata +→ not human read confirmation + +RSS item "published" +→ feed.item.published +→ not participant awareness + +XMPP stream acknowledgement +→ notification.attempt.accepted_by_provider +→ not client delivery + +Hybrid mail "sent" +→ delivery.payload.submitted or delivery.postal.handed_over only if semantics prove handover +→ not delivery confirmation +``` + +The central rule is: + +> Adapters report what happened and what it may mean. coordination-engine decides what it means for the coordination case. + +## 6. Boundary Between coordination-engine and Adapters + +### 6.1 coordination-engine Owns + +`coordination-engine` owns: + +* `CoordinationCase` +* `Participant` +* `IntendedResult` +* case-level state +* participant-level state +* result predicates +* policy evaluation +* next-action selection +* evidence ledger +* adapter selection +* scenario-specific orchestration +* closure, failure, expiry, escalation, and manual override + +### 6.2 Adapters Own + +Adapters own: + +* provider communication +* protocol-specific execution +* provider credentials and configuration +* native event ingestion +* provider-native identifiers +* native status interpretation +* native-to-normalized event mapping +* adapter-specific retries where appropriate +* adapter-specific diagnostics +* adapter health +* adapter-native evidence assessment + +### 6.3 Shared Contract + +The shared contract consists of: + +* `AdapterDescriptor` +* `AdapterCapabilityProfile` +* `AdapterActionCapability` +* `AdapterActionRequest` +* `AdapterActionResult` +* `AdapterEvent` +* `EvidenceEvent` +* `AdapterEvidenceAssessment` +* `EvidenceGrade` +* `EvidenceCeiling` +* `NativeStatusMapping` +* `NativeEventSemantics` +* `EndpointQuality` +* `AdapterHealth` +* `CorrelationContext` +* `ErrorDescriptor` +* `LateEventPolicy` +* `FeatureDependency` +* `AssuranceCapability` +* `AdapterGoldenTest` + +## 7. Adapter Types + +An adapter may belong to one or more adapter types. + +Recommended canonical adapter types: + +```text +notification +communication +publication +access +delivery +interaction +identity +authority +payment +signature +document +archive +crm +erp +webhook +feed +system +manual +``` + +Examples: + +| Adapter repo | Likely adapter types | +| -------------------- | ---------------------------------------------- | +| `email-connect` | notification, communication, interaction | +| `sms-connect` | notification, communication, interaction | +| `rss-connect` | publication, feed, notification | +| `xmpp-connect` | notification, communication, interaction, feed | +| `hybridmail-connect` | delivery, communication, document, archive | +| `portal-connect` | access, delivery, interaction, identity | +| `payment-connect` | payment, interaction, delivery | +| `signature-connect` | signature, identity, authority, delivery | +| `identity-connect` | identity, authority | +| `webhook-connect` | system, delivery, interaction | + +Adapter type is descriptive, not exclusive. + +## 8. Normative Language + +The following terms are used in this specification: + +* **MUST** means required for contract compliance. +* **SHOULD** means recommended unless a documented reason exists. +* **MAY** means optional. +* **MUST NOT** means prohibited for contract compliance. + +## 9. Core Interface Objects + +## 9.1 AdapterDescriptor + +An `AdapterDescriptor` declares the identity, version, capabilities, event support, evidence profile, and operational characteristics of an adapter. + +Adapters MUST expose a descriptor. + +```yaml +AdapterDescriptor: + adapter_id: string + adapter_name: string + adapter_version: string + adapter_contract_version: string + adapter_types: + - string + provider_family: string? + provider_name: string? + deployment_mode: embedded | sidecar | external | unknown + capability_profile: AdapterCapabilityProfile + supported_actions: + - AdapterActionCapability + emitted_event_types: + - string + supported_channels: + - string + supported_endpoint_types: + - string + evidence_profile: AdapterEvidenceProfile + evidence_ceiling: EvidenceCeiling + assurance_capability: AssuranceCapability + identity_profile: AdapterIdentityProfile + late_event_policy: LateEventPolicy + feature_dependencies: + - FeatureDependency + latency_profile: AdapterLatencyProfile? + cost_profile: AdapterCostProfile? + reliability_profile: AdapterReliabilityProfile? + configuration_schema_ref: string? + native_status_mapping_ref: string? + golden_tests_ref: string? + health_endpoint_ref: string? + documentation_ref: string? + limitations: + - string +``` + +### Required Fields + +The following fields are REQUIRED: + +```text +adapter_id +adapter_name +adapter_version +adapter_contract_version +adapter_types +capability_profile +supported_actions +emitted_event_types +supported_channels +supported_endpoint_types +evidence_profile +evidence_ceiling +assurance_capability +identity_profile +late_event_policy +limitations +``` + +## 9.2 AdapterCapabilityProfile + +An `AdapterCapabilityProfile` declares what the adapter can do, observe, and never prove. + +```yaml +AdapterCapabilityProfile: + can_notify: boolean + can_publish: boolean + can_deliver_payload: boolean + can_collect_payload: boolean + can_grant_access: boolean + can_revoke_access: boolean + can_observe_interaction: boolean + can_observe_identity: boolean + can_observe_authority: boolean + can_emit_delivery_receipts: boolean + can_emit_display_or_read_receipts: boolean + can_emit_return_or_failure_events: boolean + can_emit_late_events: boolean + can_cancel_after_dispatch: boolean + supports_webhooks: boolean + supports_polling: boolean + supports_idempotency: native | adapter_managed | none | unknown + supports_endpoint_quality: boolean + supports_native_status_mapping: boolean + supports_golden_tests: boolean + limitations: + - string +``` + +### Example: email-connect + +```yaml +AdapterCapabilityProfile: + can_notify: true + can_publish: false + can_deliver_payload: false + can_collect_payload: false + can_grant_access: false + can_revoke_access: false + can_observe_interaction: true + can_observe_identity: false + can_observe_authority: false + can_emit_delivery_receipts: false + can_emit_display_or_read_receipts: false + can_emit_return_or_failure_events: true + can_emit_late_events: true + can_cancel_after_dispatch: false + supports_webhooks: true + supports_polling: false + supports_idempotency: adapter_managed + supports_endpoint_quality: true + supports_native_status_mapping: true + supports_golden_tests: true + limitations: + - Recipient mail server acceptance does not prove inbox placement. + - Open and click tracking are ambiguous. +``` + +### Example: hybridmail-connect + +```yaml +AdapterCapabilityProfile: + can_notify: false + can_publish: false + can_deliver_payload: true + can_collect_payload: false + can_grant_access: false + can_revoke_access: false + can_observe_interaction: false + can_observe_identity: false + can_observe_authority: false + can_emit_delivery_receipts: true + can_emit_display_or_read_receipts: false + can_emit_return_or_failure_events: true + can_emit_late_events: true + can_cancel_after_dispatch: false + supports_webhooks: true + supports_polling: true + supports_idempotency: adapter_managed + supports_endpoint_quality: true + supports_native_status_mapping: true + supports_golden_tests: true + limitations: + - Postal handover does not prove recipient reading. + - Ordinary letters often do not provide final delivery confirmation. +``` + +## 9.3 EvidenceCeiling + +An `EvidenceCeiling` declares the strongest evidence the adapter can provide under its configured capabilities. + +```yaml +EvidenceCeiling: + max_positive_event: string + max_positive_strength: none | weak | medium | strong | conclusive | unknown + can_prove_human_awareness: boolean | partially + can_prove_payload_access: boolean | partially + can_prove_payload_delivery: boolean | partially + can_prove_identity: boolean | partially + can_prove_authority: boolean | partially + can_prove_non_repudiation: boolean | partially + conditions: + - string + limitations: + - string +``` + +### Examples + +```yaml +email: + max_positive_event: interaction.unverified_actor_interaction + max_positive_strength: medium + can_prove_human_awareness: false + can_prove_payload_access: false + can_prove_payload_delivery: false + can_prove_identity: false + can_prove_authority: false + can_prove_non_repudiation: false + limitations: + - Email tracking cannot reliably prove intended-recipient awareness. +``` + +```yaml +sms: + max_positive_event: notification.endpoint.accepted + max_positive_strength: strong + can_prove_human_awareness: false + can_prove_payload_access: false + can_prove_payload_delivery: false + can_prove_identity: false + can_prove_authority: false + can_prove_non_repudiation: false + conditions: + - Strongest evidence depends on carrier delivery receipts. +``` + +```yaml +xmpp_with_display_markers: + max_positive_event: interaction.notification.opened + max_positive_strength: strong + can_prove_human_awareness: partially + can_prove_payload_access: false + can_prove_payload_delivery: false + can_prove_identity: partially + can_prove_authority: false + can_prove_non_repudiation: false + conditions: + - Requires client support for displayed markers. +``` + +```yaml +hybridmail_registered: + max_positive_event: delivery.postal.delivery_confirmed + max_positive_strength: strong + can_prove_human_awareness: false + can_prove_payload_access: partially + can_prove_payload_delivery: partially + can_prove_identity: false + can_prove_authority: false + can_prove_non_repudiation: partially + conditions: + - Requires registered or delivery-confirmation postal product. +``` + +## 9.4 AssuranceCapability + +`AssuranceCapability` allows coordination-engine to compare adapter capabilities against scenario assurance requirements. + +```yaml +AssuranceCapability: + awareness_assurance: none | weak | medium | strong | conclusive | conditional | unknown + delivery_assurance: none | weak | medium | strong | conclusive | conditional | unknown + identity_assurance: none | weak | medium | strong | conclusive | conditional | unknown + authority_assurance: none | weak | medium | strong | conclusive | conditional | unknown + non_repudiation_assurance: none | weak | medium | strong | conclusive | conditional | unknown + conditions: + - string + limitations: + - string +``` + +Example: + +```yaml +email: + awareness_assurance: weak + delivery_assurance: none + identity_assurance: none + authority_assurance: none + non_repudiation_assurance: none +``` + +```yaml +portal: + awareness_assurance: strong + delivery_assurance: strong + identity_assurance: strong + authority_assurance: conditional + non_repudiation_assurance: medium +``` + +## 9.5 AdapterActionCapability + +An `AdapterActionCapability` declares one action an adapter can perform. + +```yaml +AdapterActionCapability: + action_type: string + description: string? + mode: sync | async | scheduled + idempotency_required: boolean + required_fields: + - string + optional_fields: + - string + supported_channels: + - string + supported_endpoint_types: + - string + preconditions: + - string + postconditions: + - string + capability_dependencies: + - string + unsupported_when: + - string + expected_initial_events: + - string + possible_later_events: + - string + known_failure_modes: + - string +``` + +### Action Type Naming + +Action types SHOULD use dotted semantic names. + +Recommended families: + +```text +notification.* +publication.* +access.* +delivery.* +interaction.* +identity.* +authority.* +payment.* +signature.* +document.* +archive.* +feed.* +webhook.* +recipient.* +system.* +``` + +Examples: + +```text +notification.send +notification.send_reminder +publication.publish +access.grant +access.revoke +delivery.submit_payload +delivery.submit_letter +delivery.request_submission +payment.create_request +payment.cancel_request +signature.create_envelope +signature.cancel_envelope +feed.publish_item +recipient.suppress +recipient.unsuppress +``` + +## 9.6 AdapterActionRequest + +An `AdapterActionRequest` is a command from `coordination-engine` to an adapter. + +Adapters MUST accept action requests in this conceptual shape, even if transport-specific serialization differs. + +```yaml +AdapterActionRequest: + request_id: string + action_type: string + coordination_case_id: string + participant_id: string? + payload_ref: ResourceRef? + action_surface_ref: ActionSurfaceRef? + channel: string? + target_endpoint: EndpointRef? + actor_context: ActorContext? + content: ActionContent? + template_ref: string? + variables: object? + tracking_context: TrackingContext + policy_context: PolicyContext? + security_context: SecurityContext? + idempotency_key: string + requested_at: timestamp + expires_at: timestamp? + metadata: object? +``` + +### Required Fields + +The following fields are REQUIRED: + +```text +request_id +action_type +coordination_case_id +tracking_context +idempotency_key +requested_at +``` + +Fields such as `participant_id`, `payload_ref`, `target_endpoint`, or `action_surface_ref` may be optional for system-level, broadcast, or publication actions, but adapters MAY require them for specific action types. + +## 9.7 Idempotency + +`idempotency_key` MUST be provided for all externally visible actions. + +Adapters MUST treat repeated action requests with the same `idempotency_key` as duplicates of the same logical request. + +Adapters SHOULD return the original or current action result for duplicate requests. + +For irreversible or expensive side-effect actions, such as hybrid-mail physical dispatch or payment creation, adapters MUST implement strict duplicate prevention. + +If the same idempotency key is used with conflicting request content, the adapter MUST return an idempotency conflict. + +```yaml +IdempotencyConflict: + idempotency_key: string + original_request_ref: string + conflicting_request_ref: string + conflict_fields: + - string +``` + +## 9.8 TrackingContext + +`tracking_context` MUST contain enough information to correlate later adapter events back to the coordination case. + +```yaml +TrackingContext: + correlation_id: string + coordination_case_id: string + participant_id: string? + notification_id: string? + delivery_id: string? + interaction_id: string? + payload_id: string? + action_surface_id: string? + external_context: + - key: string + value: string +``` + +Adapters MUST preserve correlation identifiers where technically possible. + +Adapters SHOULD include correlation identifiers in outbound provider metadata, message headers, webhook metadata, URLs, tokens, provider custom arguments, or provider-specific reference fields when supported. + +## 9.9 EndpointRef + +```yaml +EndpointRef: + endpoint_id: string? + endpoint_type: string + value: string? + display_name: string? + verified: boolean? + metadata: object? +``` + +Examples of `endpoint_type`: + +```text +email_address +phone_number +push_token +xmpp_jid +rss_feed +atom_feed +webhook_url +portal_account +payment_account +signature_recipient +api_client +postal_address +physical_address +muc_room_jid +pubsub_node +``` + +## 9.10 EndpointQuality + +Adapters SHOULD expose endpoint quality where meaningful. + +```yaml +EndpointQuality: + endpoint_ref: EndpointRef + endpoint_type: string + validity: valid | invalid | suspected_invalid | unknown + reachability: reachable | unreachable | degraded | unknown + verification_state: unverified | syntax_validated | provider_verified | recently_successful | failed | unknown + suppression_state: active | suppressed | blocked | opted_out | unknown + last_success_at: timestamp? + last_failure_at: timestamp? + quality_signals: + - string + confidence: low | medium | high + adapter_specific: + email: object? + sms: object? + rss: object? + xmpp: object? + hybridmail: object? + other: object? +``` + +Examples of adapter-specific quality signals: + +```text +email: mx_exists, hard_bounce_history, catch_all_suspected +sms: e164_valid, number_type, carrier, opt_out_state +rss: feed_valid, last_fetch_observed, cache_status +xmpp: presence_state, roster_state, resource_known +hybridmail: address_valid, address_position_valid, return_history +``` + +## 9.11 ActionSurfaceRef + +Adapters often point participants toward action surfaces rather than completing results themselves. + +```yaml +ActionSurfaceRef: + action_surface_id: string + action_surface_type: portal | payment_page | signature_flow | upload_form | api_endpoint | physical_letter | chat_command | feed_item | other + uri: string? + requires_authentication: boolean? + expected_interactions: + - string + metadata: object? +``` + +Adapters SHOULD include `action_surface_ref` when the action contains a link, command, form, payment flow, signature flow, or physical action prompt. + +## 9.12 ActionContent + +`ActionContent` represents content or content references. + +Adapters SHOULD prefer references over large inline content where possible. + +```yaml +ActionContent: + content_ref: string? + subject: string? + body_text: string? + body_html: string? + payload_refs: + - ResourceRef + action_links: + - ActionLink + attachments: + - ResourceRef + inline_data: object? +``` + +## 9.13 ResourceRef + +```yaml +ResourceRef: + ref_type: string + ref_id: string + uri: string? + version: string? + integrity_hash: string? + metadata: object? +``` + +## 9.14 ActionLink + +```yaml +ActionLink: + link_id: string + purpose: string + url: string + expires_at: timestamp? + tokenized: boolean? + requires_authentication: boolean? +``` + +## 9.15 AdapterActionResult + +An `AdapterActionResult` is the adapter’s response to an action request. + +```yaml +AdapterActionResult: + request_id: string + adapter_id: string + accepted: boolean + action_state: AdapterActionState + adapter_operation_id: string? + provider_operation_id: string? + initial_events: + - EvidenceEvent + raw_response_ref: string? + error: ErrorDescriptor? + received_at: timestamp + completed_at: timestamp? + metadata: object? +``` + +### AdapterActionState + +```text +accepted +rejected +queued +scheduled +in_progress +completed +failed +cancelled +duplicate +idempotency_conflict +unknown +``` + +### Important Rule + +An accepted action result is not proof that the intended result was achieved. + +Example: + +For email, `accepted: true` means the email adapter accepted the send request. It does not prove that the recipient received, saw, understood, or acted on the notification. + +For hybrid mail, `accepted: true` means the adapter accepted the request. It does not prove that the letter was validated, printed, handed over, delivered, or read. + +## 9.16 AdapterEvent + +An `AdapterEvent` is an incoming event from an adapter before or during evidence normalization. + +Adapters MAY expose native-like adapter events, but MUST provide a mapping to `EvidenceEvent`. + +```yaml +AdapterEvent: + event_id: string + adapter_id: string + adapter_event_type: string + native_event_type: string? + native_status: string? + occurred_at: timestamp? + received_at: timestamp + observation: EventObservation + correlation: CorrelationContext + participant_id: string? + payload_ref: ResourceRef? + action_surface_ref: ActionSurfaceRef? + endpoint_ref: EndpointRef? + actor_context: ActorContext? + provider_refs: + - ProviderRef + raw_event_ref: string? + normalization_status: normalized | ignored | failed | pending + normalized_events: + - EvidenceEvent + metadata: object? +``` + +## 9.17 EventObservation + +`EventObservation` distinguishes the event source from the event meaning. + +```yaml +EventObservation: + source_type: adapter | provider_api | provider_webhook | endpoint | carrier | recipient_client | postal_carrier | feed_server | action_surface | manual | system | unknown + observed_at: timestamp + occurred_at: timestamp? + source_confidence: low | medium | high + observer_ref: string? + transport_ref: string? + notes: + - string +``` + +This is useful because provider events may be delayed, forwarded, batched, inferred, or partially observed. + +## 9.18 EvidenceEvent + +An `EvidenceEvent` is the normalized event format consumed by the `evidence-ledger`. + +Adapters MUST produce or enable production of `EvidenceEvent` records. + +```yaml +EvidenceEvent: + evidence_event_id: string + event_type: string + event_family: string + occurred_at: timestamp? + observed_at: timestamp + source_adapter_id: string? + source_adapter_event_id: string? + coordination_case_id: string + participant_id: string? + payload_ref: ResourceRef? + action_surface_ref: ActionSurfaceRef? + endpoint_ref: EndpointRef? + actor_context: ActorContext? + observation: EventObservation? + provider_refs: + - ProviderRef + normalized_meaning: string + evidence_grade: EvidenceGrade + confidence: ConfidenceGrade + raw_event_ref: string? + correlation: CorrelationContext + native_status_mapping_ref: string? + metadata: object? +``` + +### Required Fields + +The following fields are REQUIRED: + +```text +evidence_event_id +event_type +event_family +observed_at +coordination_case_id +normalized_meaning +evidence_grade +confidence +correlation +``` + +`occurred_at` SHOULD be included when the source provides it. + +## 10. Canonical Event Families + +The following event families are canonical for adapter-generated evidence. + +```text +notification +publication +access +delivery +interaction +identity +authority +payload +payment +signature +feed +webhook +system +manual +result +policy +``` + +Adapters SHOULD map native events into these families. + +## 11. Canonical Event Types + +This version defines a starter vocabulary. It is not exhaustive. + +## 11.1 Notification Events + +```text +notification.attempt.created +notification.attempt.accepted_by_adapter +notification.attempt.rejected_by_adapter +notification.attempt.accepted_by_provider +notification.attempt.rejected_by_provider +notification.attempt.queued +notification.attempt.scheduled +notification.attempt.delayed +notification.attempt.cancelled +notification.endpoint.accepted +notification.endpoint.deferred +notification.endpoint.rejected_temporary +notification.endpoint.rejected_permanent +notification.endpoint.unreachable +notification.endpoint.unknown +notification.channel.complaint_received +notification.channel.unsubscribe_received +notification.channel.suppression_added +notification.channel.suppression_removed +notification.channel.reputation_warning +``` + +## 11.2 Publication and Feed Events + +```text +publication.item.published +publication.item.updated +publication.item.removed +feed.item.published +feed.item.updated +feed.item.removed +feed.item.expired +feed.item.fetched +feed.feed.fetched +feed.feed.validated +feed.feed.validation_failed +feed.hub.notified +feed.hub.notification_failed +``` + +## 11.3 Access Events + +```text +access.grant.created +access.grant.issued +access.grant.used +access.grant.denied +access.grant.expired +access.grant.revoked +access.grant.delegated +access.grant.suspicious_use_detected +``` + +## 11.4 Delivery Events + +```text +delivery.payload.available +delivery.payload.presented +delivery.payload.viewed +delivery.payload.retrieved +delivery.payload.downloaded +delivery.payload.submitted +delivery.payload.received +delivery.payload.validated +delivery.payload.validation_failed +delivery.payload.accepted +delivery.payload.rejected +delivery.payload.archived +delivery.payload.failed +delivery.production.started +delivery.production.completed +delivery.production.failed +delivery.postal.handed_over +delivery.postal.in_transit +delivery.postal.delivery_confirmed +delivery.postal.undeliverable +delivery.postal.return_received +delivery.postal.forwarded +delivery.postal.address_corrected +delivery.postal.status_unknown +delivery.postal.expired_without_final_status +``` + +The event `delivery.postal.delivered` SHOULD be avoided because it is ambiguous. Use `delivery.postal.delivery_confirmed` where actual confirmation semantics exist. + +## 11.5 Interaction Events + +```text +interaction.surface.opened +interaction.notification.opened +interaction.link.clicked +interaction.proxy_or_privacy_interaction +interaction.scanner_or_bot_interaction +interaction.unverified_actor_interaction +interaction.authenticated_actor_interaction +interaction.authorized_actor_interaction +interaction.reply_received +interaction.out_of_office_received +interaction.acknowledgement_recorded +interaction.decline_recorded +interaction.dispute_opened +``` + +## 11.6 Identity Events + +```text +identity.actor_unknown +identity.actor_suspected +identity.actor_authenticated +identity.actor_mfa_verified +identity.actor_verified_by_provider +identity.actor_mismatch_detected +``` + +## 11.7 Authority Events + +```text +authority.unknown +authority.self_asserted +authority.delegate_authorized +authority.organizational_representative_verified +authority.signer_authorized +authority.payment_authorized +authority.authorization_failed +``` + +## 11.8 Payment Events + +```text +payment.request.created +payment.request.presented +payment.started +payment.failed +payment.cancelled +payment.succeeded +payment.settled +payment.partially_settled +payment.refunded +payment.disputed +payment.promise_to_pay_recorded +``` + +## 11.9 Signature Events + +```text +signature.envelope.created +signature.envelope.sent +signature.document.viewed +signature.signing.started +signature.signing.completed +signature.signing.declined +signature.signing.expired +signature.signing.cancelled +signature.document.finalized +signature.document.archived +``` + +## 11.10 Webhook/System Events + +```text +webhook.request.sent +webhook.response.accepted +webhook.response.rejected +webhook.delivery.failed +system.adapter.health_changed +system.provider.degraded +system.provider.unavailable +``` + +## 12. Acceptance-Level Metadata + +Some generic event names, especially `notification.endpoint.accepted`, may represent different strengths depending on protocol. + +Adapters SHOULD include acceptance-level metadata. + +```yaml +AcceptanceSemantics: + acceptance_level: provider | relay | carrier | server | endpoint | client | device | mailbox | physical_handover | unknown + finality: preliminary | intermediate | final | unknown + does_not_mean: + - string +``` + +Examples: + +```yaml +email_mx_accepted: + acceptance_level: server + finality: intermediate + does_not_mean: + - inbox placement + - human awareness +``` + +```yaml +sms_delivered: + acceptance_level: device + finality: final + does_not_mean: + - human read + - intended-recipient identity +``` + +```yaml +xmpp_delivery_receipt: + acceptance_level: client + finality: intermediate + does_not_mean: + - displayed + - understood +``` + +```yaml +hybridmail_postal_handover: + acceptance_level: physical_handover + finality: intermediate + does_not_mean: + - final delivery confirmation + - human reading +``` + +## 13. EvidenceGrade + +An `EvidenceGrade` describes what an event can reasonably support. + +Adapters MUST assign an evidence grade to normalized evidence events. + +```yaml +EvidenceGrade: + strength: none | weak | medium | strong | conclusive | negative | ambiguous + actor_certainty: none | low | medium | high + authority_certainty: none | low | medium | high + payload_certainty: none | low | medium | high + interaction_certainty: none | low | medium | high + timing_certainty: none | low | medium | high + channel_certainty: none | low | medium | high + non_repudiation_strength: none | low | medium | high + notes: + - string +``` + +## 14. ConfidenceGrade + +`ConfidenceGrade` expresses how confident the adapter is in the normalized interpretation. + +```yaml +ConfidenceGrade: + level: low | medium | high + basis: + - string +``` + +Example: + +```yaml +level: medium +basis: + - Provider event type is known. + - Native event contains provider message ID. + - Event was correlated to known action request. +``` + +## 15. NativeStatusMapping + +Adapters MUST define native-to-normalized mappings for provider statuses or native events where applicable. + +```yaml +NativeStatusMapping: + native_status: string + native_event_type: string? + native_status_scope: provider | endpoint | transport | interaction | delivery | production | postal | payment | signature | publication | feed | system | unknown + normalized_event: string + normalized_state: string? + evidence_grade: EvidenceGrade + confidence: low | medium | high + terminal: boolean + retryable: boolean? + ambiguous: boolean + product_or_feature_dependent: boolean + required_features: + - string + warning: string? + examples: + - string +``` + +## 16. NativeEventSemantics + +Adapters SHOULD document native event semantics for agent-friendly implementation. + +```yaml +NativeEventSemantics: + native_event_type: string + source_system: string + plain_meaning: string + does_not_mean: + - string + normalized_mapping: string + evidence_grade: EvidenceGrade + examples: + - string +``` + +Example: + +```yaml +native_event_type: delivered +source_system: email_provider +plain_meaning: Recipient mail server accepted the message. +does_not_mean: + - Message reached inbox. + - Human recipient saw the message. + - Payload was accessed. +normalized_mapping: notification.endpoint.accepted +``` + +## 17. AdapterEvidenceAssessment + +Adapters SHOULD provide a native evidence assessment for standalone use and diagnostics. + +```yaml +AdapterEvidenceAssessment: + subject_ref: string + coordination_case_id: string? + participant_id: string? + adapter_id: string + category: success | fail | undef + subclass: string + confidence: low | medium | high + strongest_signal: string? + evidence_summary: + - string + recommended_coordination_interpretation: string? + limitations: + - string +``` + +Adapter assessment is advisory. + +`coordination-engine` remains authoritative for participant and case state. + +Examples: + +```text +email: + category: undef + subclass: endpoint_accepted_only + +sms: + category: success + subclass: delivered + +hybridmail: + category: success + subclass: handed_to_postal_service +``` + +These adapter-native classifications do not automatically become coordination-case success. + +## 18. ActorContext + +`ActorContext` describes the actor associated with an event, if known. + +```yaml +ActorContext: + actor_ref: string? + actor_type: human | organization | system | agent | bot | unknown + identity_strength: none | low | medium | high + authority_strength: none | low | medium | high + authenticated: boolean? + delegated: boolean? + provider_identity_ref: string? + metadata: object? +``` + +Adapters SHOULD avoid overclaiming identity. + +For example: + +* `email-connect` SHOULD NOT mark a click as authenticated unless backed by trusted identity or portal evidence. +* `sms-connect` SHOULD NOT assume phone possession equals intended participant identity. +* `rss-connect` SHOULD NOT assume feed fetch equals participant identity. +* `xmpp-connect` MAY provide medium/high identity evidence if JID mapping and authentication are reliable. +* `hybridmail-connect` SHOULD NOT infer recipient identity merely from postal dispatch. + +## 19. CorrelationContext + +Correlation is mandatory for useful adapter integration. + +```yaml +CorrelationContext: + correlation_id: string + request_id: string? + coordination_case_id: string + participant_id: string? + notification_id: string? + delivery_id: string? + interaction_id: string? + payload_id: string? + action_surface_id: string? + adapter_operation_id: string? + provider_operation_id: string? + provider_message_id: string? + external_refs: + - ProviderRef +``` + +Adapters MUST preserve correlation identifiers where technically possible. + +## 20. ProviderRef + +```yaml +ProviderRef: + provider_name: string + ref_type: string + ref_value: string +``` + +Examples: + +```yaml +provider_name: sendgrid +ref_type: message_id +ref_value: abc123 +``` + +```yaml +provider_name: stripe +ref_type: payment_intent_id +ref_value: pi_123 +``` + +```yaml +provider_name: pingen +ref_type: letter_id +ref_value: letter_123 +``` + +## 21. FeatureDependency + +`FeatureDependency` describes provider-, route-, product-, or configuration-specific requirements. + +```yaml +FeatureDependency: + feature_name: string + dependency_type: provider | product | route | country | endpoint | recipient_client | configuration | policy | account | unknown + required_for_events: + - string + fallback_behavior: string + limitation: string +``` + +Examples: + +```yaml +feature_name: email_click_tracking +dependency_type: configuration +required_for_events: + - interaction.link.clicked +fallback_behavior: no click events emitted +limitation: Link clicks cannot be observed without tracking links. +``` + +```yaml +feature_name: xmpp_displayed_markers +dependency_type: recipient_client +required_for_events: + - interaction.notification.opened +fallback_behavior: delivery receipts only +limitation: Displayed markers depend on client support. +``` + +```yaml +feature_name: hybridmail_delivery_confirmation +dependency_type: product +required_for_events: + - delivery.postal.delivery_confirmed +fallback_behavior: postal handover is maximum positive evidence +limitation: Ordinary letters do not provide delivery confirmation. +``` + +## 22. LateEventPolicy + +Adapters frequently produce late events. + +Examples: + +| Adapter | Late event example | +| ----------- | ------------------------ | +| Email | async bounce | +| SMS | late delivery receipt | +| RSS | delayed feed fetch log | +| XMPP | delayed delivery receipt | +| Hybrid mail | return mail weeks later | + +Adapters MUST declare their late-event policy. + +```yaml +LateEventPolicy: + accepts_late_events: boolean + late_events_can_revise_assessment: boolean + late_events_can_reopen_case: boolean + default_late_event_window: string? + expected_late_event_types: + - string + notes: + - string +``` + +Normative rule: + +```text +Adapters MUST emit late events if they are relevant. +coordination-engine decides whether to revise, annotate, reopen, or ignore case state. +``` + +## 23. ErrorDescriptor + +```yaml +ErrorDescriptor: + code: string + message: string + retryable: boolean + category: validation | authentication | authorization | configuration | provider | network | rate_limit | timeout | conflict | idempotency_conflict | unsupported_capability | unknown + native_error_code: string? + native_error_message: string? + details: object? +``` + +Adapters MUST return an `ErrorDescriptor` when an action request is rejected or fails before acceptance. + +## 24. AdapterHealth + +Adapters MUST expose health state. + +```yaml +AdapterHealth: + adapter_id: string + status: healthy | degraded | unavailable | misconfigured | unknown + checked_at: timestamp + provider_connectivity: healthy | degraded | unavailable | unknown + configuration_validity: valid | invalid | partial | unknown + credential_status: valid | expired | missing | insufficient | unknown + webhook_status: healthy | degraded | unavailable | not_supported | unknown + polling_status: healthy | degraded | unavailable | not_supported | unknown + last_action_success_at: timestamp? + last_event_received_at: timestamp? + known_degradations: + - string + metrics: + actions_accepted_last_24h: integer? + actions_failed_last_24h: integer? + events_received_last_24h: integer? + average_action_latency_ms: integer? +``` + +## 25. Transport Patterns + +This specification does not mandate one technical transport. + +Adapters MAY integrate through: + +```text +REST API +event bus +message queue +webhook +gRPC +embedded library +CLI +file drop +polling +``` + +However, any transport MUST preserve the semantic contract defined here. + +## 26. Adapter Integration Modes + +## 26.1 Embedded Mode + +Adapter code runs in the same process as `coordination-engine`. + +Suitable for: + +* local development +* test adapters +* simulated adapters +* simple deployments + +## 26.2 Sidecar Mode + +Adapter runs next to the engine but is deployed separately. + +Suitable for: + +* credential isolation +* provider-specific webhook handling +* protocol-specific runtime dependencies + +## 26.3 External Service Mode + +Adapter is an independently deployed service. + +Suitable for: + +* reusable `*-connect` components +* multi-tenant installations +* independent scaling +* provider abstraction +* operational separation + +Preferred direction for production adapters: + +```text +*-connect repositories SHOULD implement external service mode first, +while optionally also offering embedded or sidecar modes. +``` + +## 27. Adapter Lifecycle + +Adapters SHOULD support the following lifecycle: + +```text +registered +configured +healthy +degraded +disabled +retired +``` + +### 27.1 Registration + +During registration, the adapter provides its `AdapterDescriptor`. + +### 27.2 Configuration + +Configuration may include credentials, provider selection, endpoint settings, webhook secrets, templates, routing policies, tenant-specific settings, product settings, or feature flags. + +### 27.3 Health Monitoring + +The engine or platform periodically checks `AdapterHealth`. + +### 27.4 Action Dispatch + +The engine dispatches `AdapterActionRequest` objects to the adapter. + +### 27.5 Event Ingestion + +The adapter emits `AdapterEvent` or `EvidenceEvent` objects. + +### 27.6 Degradation Handling + +If an adapter is degraded, the policy engine may switch channel, delay actions, retry later, or escalate. + +## 28. Event Ordering, Idempotency, and Late Events + +### 28.1 Event Ordering + +Adapters MUST NOT assume events arrive in causal order. + +The engine MUST be able to process late events. + +Adapters SHOULD include accurate `occurred_at` and `observed_at` timestamps. + +### 28.2 Duplicate Events + +Adapters SHOULD provide stable event identifiers where possible. + +The engine MUST deduplicate events using: + +```text +evidence_event_id +adapter_event_id +provider event id +correlation context +event type +timestamp +``` + +### 28.3 Late Events + +Late events MUST still be accepted and recorded if they are relevant. + +Examples: + +```text +Email: late hard bounce +SMS: late delivery receipt +RSS: delayed fetch log +XMPP: delayed receipt after offline delivery +Hybrid mail: return mail event after postal handover +``` + +### 28.4 Idempotent Actions + +All externally visible actions MUST include `idempotency_key`. + +Adapters MUST avoid duplicate external side effects for repeated action requests with the same key. + +## 29. Adapter Conformance Levels + +Adapters MAY implement different maturity levels. + +```text +Level 0: Descriptor only / simulated +Level 1: Action execution + basic evidence events +Level 2: Native status mapping + health + idempotency +Level 3: Evidence grading + endpoint quality + late events +Level 4: Full provider flavor / product semantics / golden tests +``` + +### Level 0 + +The adapter exposes a descriptor and may simulate actions/events. + +### Level 1 + +The adapter can execute at least one supported action and emit basic normalized evidence events. + +### Level 2 + +The adapter supports health, idempotency, native status mapping, and error handling. + +### Level 3 + +The adapter supports evidence grading, endpoint quality, late events, and advisory assessments. + +### Level 4 + +The adapter includes provider/product semantics, capability profiles, native status mapping registry, golden tests, and conformance documentation. + +Production adapters SHOULD target Level 3 or higher. + +Provider-flavor-heavy adapters, such as `hybridmail-connect`, SHOULD target Level 4. + +## 30. Golden Test Requirements + +Every adapter SHOULD define golden test scenarios. + +```yaml +AdapterGoldenTest: + test_id: string + scenario: string + adapter_id: string + native_events: + - object + expected_evidence_events: + - EvidenceEvent + expected_assessment: AdapterEvidenceAssessment + must_not_emit: + - string + notes: + - string +``` + +Recommended test categories: + +```text +duplicate action request +idempotency conflict +provider acceptance +provider rejection +ambiguous native event +late event +out-of-order event +unsupported capability +endpoint failure +strongest supported positive event +status overclaim prevention +raw event preservation +``` + +Example: + +```yaml +test_id: hybridmail_ordinary_handover_not_delivery_confirmation +scenario: Ordinary letter handed over to postal service. +expected_evidence_events: + - event_type: delivery.postal.handed_over +must_not_emit: + - delivery.postal.delivery_confirmed +``` + +Example: + +```yaml +test_id: email_provider_delivered_not_awareness +scenario: Email provider reports delivered. +expected_evidence_events: + - event_type: notification.endpoint.accepted +must_not_emit: + - interaction.notification.opened + - delivery.payload.downloaded +``` + +## 31. Security Requirements + +Adapters MUST: + +* protect provider credentials +* verify incoming provider webhooks where possible +* validate action request authenticity +* avoid leaking sensitive payloads in logs +* support least-privilege configuration +* preserve correlation without exposing unnecessary personal data +* avoid storing raw sensitive data unless explicitly configured +* distinguish test/sandbox from production where relevant + +Adapters SHOULD: + +* support tenant separation +* support secret rotation +* support signed callbacks or event signatures +* support audit logging of action execution + +## 32. Privacy Requirements + +Adapters SHOULD minimize personal data. + +Adapters SHOULD support: + +* endpoint references instead of full endpoint values where possible +* redaction of raw provider events +* retention limits for raw event data +* configurable storage of message content +* metadata-only mode +* data deletion or anonymization workflows where applicable + +The adapter interface MUST allow references to external payloads rather than requiring payload content to be copied into the adapter. + +## 33. Standalone Adapter Requirements + +Adapters are encouraged to be useful outside `coordination-engine`. + +A standalone adapter MAY provide: + +* provider abstraction +* operational dashboard +* message or delivery timeline API +* diagnostic API +* native event archive +* normalized event stream +* suppression management +* provider health checks +* local policy hints +* endpoint quality diagnostics +* adapter-native evidence assessment + +However, standalone features MUST NOT contradict the coordination-engine contract. + +For example: + +* `email-connect` may expose `delivered_to_mx`, but MUST NOT expose it as business-level awareness. +* `sms-connect` may expose `delivered_to_device`, but MUST NOT expose it as human-read proof. +* `rss-connect` may expose `published`, but MUST NOT expose it as participant awareness. +* `xmpp-connect` may expose `displayed`, but MUST NOT expose it as comprehension. +* `hybridmail-connect` may expose `handed_to_postal_service`, but MUST NOT expose it as final delivery confirmation. + +## 34. Compatibility Requirements for Adapter Repositories + +Every `*-connect` repository SHOULD contain: + +```text +INTENT.md +docs/AdapterImplementation.md +docs/EventMapping.md +docs/ProviderModel.md +docs/EvidenceClassification.md +docs/GoldenTests.md +schemas/ +src/ +tests/ +``` + +Every adapter repository MUST document: + +```text +Implemented adapter contract version +Conformance level +Supported actions +Emitted normalized events +Native event mappings +Evidence grading rules +Evidence ceiling +Endpoint quality model +Late event policy +Known limitations +Provider-specific assumptions +Security model +Privacy model +Standalone usage +coordination-engine integration mode +``` + +## 35. Versioning + +### 35.1 Contract Version + +Adapters MUST declare `adapter_contract_version`. + +This document defines: + +```text +adapter_contract_version: 1.1 +``` + +### 35.2 Backward Compatibility + +Minor extensions MAY add: + +* optional fields +* new event types +* new action types +* new adapter types +* new evidence metadata +* new capability flags + +Breaking changes include: + +* removing required fields +* changing meaning of existing fields +* changing event semantics +* changing idempotency requirements +* changing evidence grade semantics +* changing weakest-safe-mapping requirements + +Breaking changes require a new major version. + +## 36. Minimal Compliance Checklist + +An adapter is minimally compliant with AdapterInterfaceSpecification 1.1 if it: + +1. Exposes an `AdapterDescriptor`. +2. Exposes an `AdapterCapabilityProfile`. +3. Declares supported actions. +4. Declares emitted event types. +5. Declares an `EvidenceCeiling`. +6. Declares `AssuranceCapability`. +7. Accepts `AdapterActionRequest` objects for supported actions. +8. Returns `AdapterActionResult` objects. +9. Supports idempotency keys. +10. Emits or maps to `EvidenceEvent` objects. +11. Provides `EvidenceGrade` and `ConfidenceGrade`. +12. Preserves `CorrelationContext`. +13. Exposes `AdapterHealth`. +14. Documents known limitations. +15. Does not overclaim result success. +16. Clearly separates provider-native events from normalized evidence. +17. Applies weakest safe mapping to ambiguous native events. +18. Preserves raw native event references where possible. +19. Declares late-event behavior. +20. Provides advisory `AdapterEvidenceAssessment` or explains why not. + +## 37. Non-Goals + +This adapter specification does not define: + +* the internal storage model of adapters +* the provider-specific API shape +* the transport protocol used between engine and adapter +* legal validity of evidence +* UI requirements +* billing models +* tenant management model +* full workflow semantics +* complete event vocabulary for every possible domain + +Those concerns may be defined in separate documents or scenario-specific specifications. + +## 38. Strategic Summary + +The adapter interface allows `coordination-engine` to remain a generalized coordination runtime while integrating with many concrete systems. + +Adapters perform actions and emit evidence. They translate external provider realities into a normalized coordination vocabulary without deciding overall business success. + +Version 1.1 strengthens the interface by requiring adapters to declare what they can prove, what they cannot prove, how native statuses map to normalized evidence, and how ambiguity is handled. + +The central rule is: + +> Adapters must preserve facts, underclaim semantics, expose limitations, and let coordination-engine decide result meaning. + +This distinction is essential for reliable, extensible, evidence-driven digital coordination. + diff --git a/spec/EmailAdapterSpecification.md b/spec/EmailAdapterSpecification.md new file mode 100644 index 0000000..1dd9a65 --- /dev/null +++ b/spec/EmailAdapterSpecification.md @@ -0,0 +1,1520 @@ +# EmailAdapterSpecification.md + +## 1. Document Status + +**Document:** EmailAdapterSpecification.md +**Project:** email-connect +**Target Integration:** coordination-engine +**Adapter Contract:** AdapterInterfaceSpecification.md v1.0 +**Status:** Draft v1.0 +**Primary Scope:** Email protocol and provider integration as a coordination-engine adapter + +## 2. Purpose + +This document specifies how `email-connect` models email as a communication protocol and how it integrates with `coordination-engine`. + +`email-connect` is the reference adapter for the `coordination-engine` adapter layer. It provides email-specific sending, event ingestion, event normalization, evidence classification, message timelines, endpoint state, and provider abstraction. + +The central design objective is to make email useful as a notification and communication channel without overclaiming what email can prove. + +Email can often provide evidence that: + +* a message was accepted by an outbound provider +* a message was accepted by a recipient mail server +* a message was rejected, bounced, deferred, delayed, dropped, or suppressed +* a user agent, proxy, scanner, or recipient-like actor interacted with tracking mechanisms +* a recipient or mailbox system replied, complained, unsubscribed, or emitted an out-of-office response + +Email usually cannot prove by itself that: + +* the message reached the recipient’s inbox +* the intended human recipient saw the message +* the recipient understood the message +* the intended recipient clicked a link +* the recipient accessed the primary payload +* the coordination case result was achieved + +Therefore, `email-connect` reports email evidence. `coordination-engine` decides what that evidence means for a coordination case. + +## 3. Core Principle + +Email is a weak-to-medium evidence channel for awareness. It is not a reliable proof channel for payload delivery or intended-recipient action. + +The adapter MUST distinguish: + +```text +technical email transport evidence +mailbox or endpoint inference +engagement evidence +identity-bound interaction evidence from other systems +coordination result evidence +``` + +In the coordination model, most email events map to `undef` or weak evidence unless paired with stronger evidence from another adapter such as `portal-connect`, `identity-connect`, `signature-connect`, or `payment-connect`. + +## 4. Architectural Role + +### 4.1 Standalone Role + +As a standalone component, `email-connect` provides: + +* provider-neutral email sending +* transactional email dispatch +* provider event ingestion +* bounce classification +* complaint and unsubscribe handling +* suppression list management +* open and click tracking interpretation +* recipient endpoint diagnostics +* email message timelines +* deliverability diagnostics +* normalized email event stream +* evidence-oriented message assessment + +### 4.2 coordination-engine Adapter Role + +As a `coordination-engine` adapter, `email-connect` provides: + +#### Actions + +* `notification.send` +* `notification.send_reminder` +* `notification.register_tracking_context` +* `recipient.suppress` +* `recipient.unsuppress` +* optionally `notification.cancel` where supported by queue state +* optionally `notification.schedule` where supported + +#### Signals + +* `notification.attempt.accepted_by_adapter` +* `notification.attempt.rejected_by_adapter` +* `notification.attempt.accepted_by_provider` +* `notification.attempt.rejected_by_provider` +* `notification.attempt.queued` +* `notification.attempt.delayed` +* `notification.endpoint.accepted` +* `notification.endpoint.deferred` +* `notification.endpoint.rejected_temporary` +* `notification.endpoint.rejected_permanent` +* `notification.endpoint.unreachable` +* `notification.channel.complaint_received` +* `notification.channel.unsubscribe_received` +* `notification.channel.suppression_added` +* `notification.channel.reputation_warning` +* `interaction.notification.opened` +* `interaction.proxy_or_privacy_interaction` +* `interaction.link.clicked` +* `interaction.scanner_or_bot_interaction` +* `interaction.unverified_actor_interaction` +* `interaction.reply_received` +* `interaction.out_of_office_received` + +## 5. Relationship to coordination-engine + +`email-connect` does not own: + +* `CoordinationCase` +* intended result evaluation +* participant-level success +* case-level success +* legal delivery interpretation +* portal access +* payload retrieval +* contract acceptance +* payment settlement +* signature completion +* multi-channel escalation policy + +`email-connect` owns: + +* email send requests +* email provider abstraction +* email provider identifiers +* email event ingestion +* email event classification +* email-native status timeline +* email endpoint diagnostics +* email evidence mapping +* suppression and complaint handling +* email-specific uncertainty classification + +The boundary rule is: + +> email-connect reports what happened in the email channel and what that may indicate. coordination-engine decides what that means for the coordination case. + +## 6. Email as Notification, Not Delivery + +Within `coordination-engine`, email is primarily a **notification channel**. + +Email may contain small inline payloads, but in the primary architecture it usually points participants toward an action surface such as: + +* a portal page +* a secure document download +* a payment page +* a contract signing flow +* an approval screen +* a form submission page + +The actual payload delivery or result-relevant interaction should usually be observed by another adapter. + +Example: + +```text +email-connect: + notification.endpoint.accepted + interaction.link.clicked + +portal-connect: + identity.actor_authenticated + delivery.payload.downloaded + +coordination-engine: + participant result satisfied +``` + +Email acceptance by the recipient mail server is not payload delivery. It is technical notification evidence. + +## 7. Email Message Lifecycle Model + +`email-connect` models an email notification as a lifecycle with multiple observable phases. + +```text +message.created +message.rendered +message.render_failed +message.suppression_checked +message.send_requested +message.accepted_by_adapter +message.rejected_by_adapter +message.accepted_by_provider +message.rejected_by_provider +message.queued +message.provider_dropped +message.provider_suppressed +message.deferred +message.endpoint_accepted +message.soft_bounced +message.hard_bounced +message.async_bounced +message.delayed +message.expired_without_delivery +message.opened +message.opened_proxy_like +message.clicked +message.clicked_scanner_like +message.clicked_unverified +message.replied +message.out_of_office_received +message.complaint_received +message.unsubscribe_received +message.suppression_added +``` + +The lifecycle is not strictly linear. Events may arrive late, out of order, duplicated, or in conflict. + +Example: + +```text +provider accepted +endpoint accepted +later async bounce received +``` + +The adapter MUST preserve all meaningful events and avoid collapsing the message into a single final status too early. + +## 8. Email Attempt vs Email Message vs Recipient + +The adapter MUST distinguish at least three layers. + +### 8.1 EmailMessage + +The logical message created by a client or coordination case. + +```yaml +EmailMessage: + email_message_id: string + coordination_case_id: string? + participant_id: string? + purpose: string? + subject: string? + template_ref: string? + content_ref: string? + tracking_context: TrackingContext + created_at: timestamp +``` + +### 8.2 EmailAttempt + +One send attempt for one message through one provider/configuration. + +```yaml +EmailAttempt: + email_attempt_id: string + email_message_id: string + provider_name: string + provider_account_ref: string? + from_endpoint: EmailEndpoint + to_endpoint: EmailEndpoint + cc_endpoints: + - EmailEndpoint + bcc_endpoints: + - EmailEndpoint + provider_message_id: string? + adapter_operation_id: string? + state: EmailAttemptState + created_at: timestamp + updated_at: timestamp +``` + +### 8.3 EmailEndpoint + +The target email endpoint. + +```yaml +EmailEndpoint: + endpoint_id: string? + email_address: string + display_name: string? + endpoint_role: from | to | cc | bcc | reply_to | return_path + verification_state: unknown | syntax_validated | verified | suspected_invalid | invalid + suppression_state: active | suppressed | complaint_suppressed | bounce_suppressed | unsubscribed | unknown + metadata: object? +``` + +Per-recipient modeling is required because one email message may have multiple recipients and different outcomes per recipient. + +For coordination-engine integration, the preferred mode is one logical notification per participant endpoint. + +## 9. Email Attempt States + +The adapter SHOULD support these attempt states: + +```text +created +rendered +render_failed +suppressed +send_requested +accepted_by_adapter +rejected_by_adapter +accepted_by_provider +rejected_by_provider +queued +scheduled +provider_dropped +provider_suppressed +deferred +endpoint_accepted +soft_bounced +hard_bounced +async_bounced +delayed +expired_without_delivery +complaint_received +unsubscribe_received +unknown +``` + +These states are email-native. They are not coordination result states. + +## 10. Email Evidence Assessment + +`email-connect` should provide an email-native assessment separate from coordination-engine state. + +```yaml +EmailEvidenceAssessment: + email_message_id: string + participant_id: string? + category: success | fail | undef + subclass: string + confidence: low | medium | high + strongest_signal: string? + evidence_summary: + - string + recommended_coordination_interpretation: string? +``` + +The assessment categories are adapter-level hints. + +### 10.1 Email Adapter Success + +Email-level `success` means only that the email channel produced strong email-channel evidence. + +Possible subclasses: + +```text +success.endpoint_accepted +success.reply_received +success.human_like_open +success.human_like_click +``` + +Important: These are not automatically coordination success. + +`success.endpoint_accepted` is technical success for the email transport, but usually maps to coordination `undef.technical_acceptance_only`. + +### 10.2 Email Adapter Fail + +Email-level `fail` indicates strong evidence that the email channel failed or should not be used. + +Subclasses: + +```text +fail.invalid_address +fail.missing_address +fail.render_failed +fail.template_invalid +fail.provider_rejected +fail.provider_dropped +fail.provider_suppressed +fail.hard_bounce +fail.domain_not_found +fail.mailbox_not_found +fail.authentication_rejected +fail.policy_rejected +fail.content_rejected +fail.message_too_large +fail.complaint_received +fail.unsubscribed +fail.suppressed +fail.expired_without_delivery +``` + +### 10.3 Email Adapter Undef + +Email-level `undef` is the normal state when the adapter cannot infer reliable human awareness. + +Subclasses: + +```text +undef.pending +undef.provider_accepted_only +undef.queued +undef.deferred +undef.delayed +undef.endpoint_accepted_only +undef.no_signal +undef.weak_positive +undef.proxy_open +undef.scanner_click +undef.unverified_click +undef.identity_uncertain +undef.mailbox_possibly_reached +undef.possibly_spam_or_quarantine +undef.possibly_silent_drop +undef.forwarding_unknown +undef.catch_all_domain +undef.conflicting_evidence +undef.channel_degraded +undef.recipient_reported_missing +undef.out_of_office +``` + +The `undef` category must not be empty because email does not provide guaranteed delivery or awareness evidence. + +## 11. Detailed Email Scenario Classification + +### 11.1 Pre-Send Scenarios + +| Scenario | Email assessment | Normalized event | Notes | +| -------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------- | +| Missing address | `fail.missing_address` | `notification.attempt.rejected_by_adapter` | No send possible | +| Invalid address syntax | `fail.invalid_address` | `notification.attempt.rejected_by_adapter` | Strong local failure | +| Template rendering failure | `fail.render_failed` | `notification.attempt.rejected_by_adapter` or `payload.validation_failed` | Notification unusable | +| Invalid generated link | `fail.template_invalid` | `notification.attempt.rejected_by_adapter` | Especially relevant for portal links | +| Suppression hit | `fail.suppressed` | `notification.channel.suppression_added` or action rejected | May be due to bounce, complaint, unsubscribe | +| Adapter config missing | `fail.provider_rejected` | `system.adapter.health_changed` + action error | Operational failure | +| Provider unavailable before acceptance | `undef.pending` or `fail.provider_rejected` | action result error | Depends on retryability | +| Submission timeout | `undef.pending` | action result unknown | Could have been accepted despite timeout | + +### 11.2 Provider-Side Scenarios + +| Scenario | Email assessment | Normalized event | Notes | +| ----------------------------- | ------------------------------------------- | ------------------------------------------------------------------------ | -------------------------------- | +| Adapter accepted request | `undef.pending` | `notification.attempt.accepted_by_adapter` | Adapter accepted work | +| Provider accepted message | `undef.provider_accepted_only` | `notification.attempt.accepted_by_provider` | Sending began | +| Provider rejected message | `fail.provider_rejected` | `notification.attempt.rejected_by_provider` | Strong failure | +| Provider queued message | `undef.queued` | `notification.attempt.queued` | Pending | +| Provider delayed message | `undef.delayed` | `notification.attempt.delayed` | Pending, may degrade | +| Provider dropped message | `fail.provider_dropped` | `notification.attempt.rejected_by_provider` or dropped-specific metadata | Message may never leave provider | +| Provider suppressed recipient | `fail.provider_suppressed` | `notification.channel.suppression_added` | Channel blocked | +| Provider rate-limited | `undef.pending` | `notification.attempt.delayed` | Retry or wait | +| Provider quota exceeded | `fail.provider_rejected` or `undef.pending` | action error | Policy-dependent | + +### 11.3 Recipient Mail Server Scenarios + +| Scenario | Email assessment | Normalized event | Notes | +| ----------------------------- | --------------------------------------------------- | ------------------------------------------------------- | -------------------------------------- | +| MX accepted | `undef.endpoint_accepted_only` | `notification.endpoint.accepted` | Does not prove inbox placement | +| Temporary SMTP deferral | `undef.deferred` | `notification.endpoint.deferred` | Retry window active | +| Soft bounce | `undef.deferred` or `fail.expired_without_delivery` | `notification.endpoint.rejected_temporary` | Becomes failure after retry policy | +| Hard bounce unknown user | `fail.mailbox_not_found` | `notification.endpoint.rejected_permanent` | Strong endpoint failure | +| Domain not found | `fail.domain_not_found` | `notification.endpoint.unreachable` | Strong failure | +| SPF/DKIM/DMARC rejection | `fail.authentication_rejected` | `notification.endpoint.rejected_permanent` | Strong sender config/channel failure | +| Content policy rejection | `fail.content_rejected` | `notification.endpoint.rejected_permanent` | Strong failure | +| Recipient mailbox full | `undef.deferred` | `notification.endpoint.rejected_temporary` | May later succeed | +| Greylisting | `undef.deferred` | `notification.endpoint.deferred` | Usually retryable | +| Async bounce after acceptance | `fail.async_bounced` | `notification.endpoint.rejected_permanent` or temporary | Important late failure | +| Catch-all domain acceptance | `undef.catch_all_domain` | `notification.endpoint.accepted` + metadata | Acceptance is especially weak | +| Forwarding unknown | `undef.forwarding_unknown` | `notification.endpoint.accepted` + metadata if known | Intended recipient awareness uncertain | + +### 11.4 Mailbox and Inbox Inference Scenarios + +| Scenario | Email assessment | Notes | +| --------------------------------- | -------------------------------------------------------- | ----- | +| Delivered to inbox | Usually not directly observable | | +| Delivered to spam/junk | Usually not directly observable | | +| Delivered to promotions/other tab | Usually not directly observable | | +| Delivered then moved by user rule | Usually not directly observable | | +| Delivered then silently dropped | Usually not directly observable | | +| Quarantined by enterprise gateway | Usually not directly observable unless feedback received | | +| Accepted by shared mailbox | Awareness of intended person uncertain | | +| Accepted by inactive mailbox | Endpoint works, awareness uncertain | | +| Out-of-office reply | Mailbox likely reached, but action uncertain | | +| User reports missing email | Conflicting evidence; treat as suspicious | | +| User reports spam-folder location | Useful diagnostic; still not initial success | | + +These cases should usually map to `undef.*` because email lacks reliable inbox placement proof. + +### 11.5 Open Tracking Scenarios + +| Scenario | Email assessment | Normalized event | Notes | +| ----------------------------- | -------------------------------------------------- | -------------------------------------------- | --------------------------------- | +| No open | `undef.no_signal` | none | Absence of open proves nothing | +| Pixel loaded normally | `undef.weak_positive` or `success.human_like_open` | `interaction.notification.opened` | Weak-to-medium | +| Apple/privacy proxy-like open | `undef.proxy_open` | `interaction.proxy_or_privacy_interaction` | Not human proof | +| Gmail/image proxy open | `undef.proxy_open` or weak positive | `interaction.proxy_or_privacy_interaction` | Ambiguous | +| Security scanner image load | `undef.proxy_open` | `interaction.proxy_or_privacy_interaction` | Automated | +| Multiple human-like opens | `success.human_like_open` | `interaction.notification.opened` | Still not payload delivery | +| Open from unusual geography | `undef.identity_uncertain` | `interaction.notification.opened` + metadata | May be proxy, forwarding, or risk | + +Open tracking MUST NOT be treated as strong coordination success. + +### 11.6 Click Tracking Scenarios + +| Scenario | Email assessment | Normalized event | Notes | +| -------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------ | -------------------------------- | +| No click | `undef.no_signal` | none | No evidence of interaction | +| Scanner-like click | `undef.scanner_click` | `interaction.scanner_or_bot_interaction` | Do not count as recipient action | +| Link prefetch | `undef.scanner_click` | `interaction.scanner_or_bot_interaction` | Common in enterprise security | +| Unverified click | `undef.unverified_click` | `interaction.unverified_actor_interaction` | Identity uncertain | +| Human-like click | `success.human_like_click` | `interaction.link.clicked` | Medium, still not identity-bound | +| Click followed by authenticated portal login | email event remains weak; portal event is strong | Portal adapter provides identity evidence | | +| Click followed by payload download | email contributed path evidence; delivery evidence is decisive | Delivery/portal event closes case | | +| Token invalid after click | `fail.template_invalid` or `undef.identity_uncertain` | Depends on cause | | +| Expired link clicked | `undef.weak_positive` | Awareness possible, action path failed | | +| Forwarded link clicked | `undef.identity_uncertain` | Intended recipient unknown | | + +The adapter SHOULD classify likely scanner clicks using timing, user-agent, IP ranges, known security vendors, HEAD/GET patterns, link fan-out, and immediate multi-link access where available. + +### 11.7 Reply and Auto-Reply Scenarios + +| Scenario | Email assessment | Normalized event | Notes | +| ----------------------------------- | ---------------------------------------------- | --------------------------------------- | -------------------------------------------------------------- | +| Human reply received | `success.reply_received` | `interaction.reply_received` | Strong awareness signal, identity still may require validation | +| Out-of-office reply | `undef.out_of_office` | `interaction.out_of_office_received` | Mailbox reached, action uncertain | +| Auto-reply from ticket system | `undef.identity_uncertain` | `interaction.reply_received` + metadata | System interaction | +| Bounce-like auto-response | classify as bounce if parseable | notification endpoint event | Must parse carefully | +| Reply from delegated/shared mailbox | `undef.identity_uncertain` or medium awareness | depends on participant model | | + +### 11.8 Complaint, Unsubscribe, and Suppression + +| Scenario | Email assessment | Normalized event | Notes | +| --------------------------- | ------------------------- | ------------------------------------------- | ---------------------------------- | +| Spam complaint | `fail.complaint_received` | `notification.channel.complaint_received` | Negative channel evidence | +| Unsubscribe | `fail.unsubscribed` | `notification.channel.unsubscribe_received` | Future email use constrained | +| Bounce suppression added | `fail.suppressed` | `notification.channel.suppression_added` | Channel blocked | +| Complaint suppression added | `fail.suppressed` | `notification.channel.suppression_added` | Channel blocked | +| Manual suppression | `fail.suppressed` | `notification.channel.suppression_added` | Operator action | +| Suppression removed | active/unknown | `notification.channel.suppression_removed` | Channel may become available again | + +For legally or operationally required notifications, unsubscribe semantics must be scenario-specific. `email-connect` records the event; `coordination-engine` decides whether email remains permissible. + +## 12. Adapter-to-Coordination Mapping + +### 12.1 Core Mapping Table + +| Email-native event | Email assessment | coordination-engine event | Coordination interpretation | +| ------------------ | ------------------------------------------- | ------------------------------------------------------- | --------------------------------- | +| message created | `undef.pending` | `notification.attempt.created` | Attempt exists | +| render failed | `fail.render_failed` | `notification.attempt.rejected_by_adapter` | Notification failed before send | +| adapter accepted | `undef.pending` | `notification.attempt.accepted_by_adapter` | Work accepted | +| adapter rejected | `fail.provider_rejected` or validation fail | `notification.attempt.rejected_by_adapter` | Attempt failed | +| provider accepted | `undef.provider_accepted_only` | `notification.attempt.accepted_by_provider` | Weak send evidence | +| provider rejected | `fail.provider_rejected` | `notification.attempt.rejected_by_provider` | Strong attempt failure | +| provider queued | `undef.queued` | `notification.attempt.queued` | Pending | +| provider delayed | `undef.delayed` | `notification.attempt.delayed` | Pending/degraded | +| provider dropped | `fail.provider_dropped` | `notification.attempt.rejected_by_provider` | Strong failure | +| MX accepted | `undef.endpoint_accepted_only` | `notification.endpoint.accepted` | Technical delivery only | +| temporary deferral | `undef.deferred` | `notification.endpoint.deferred` | Pending | +| soft bounce | `undef.deferred` | `notification.endpoint.rejected_temporary` | Retryable/pending | +| hard bounce | `fail.hard_bounce` | `notification.endpoint.rejected_permanent` | Strong failure | +| async bounce | `fail.async_bounced` | `notification.endpoint.rejected_permanent` or temporary | Late failure | +| open human-like | `success.human_like_open` | `interaction.notification.opened` | Weak-to-medium awareness | +| open proxy-like | `undef.proxy_open` | `interaction.proxy_or_privacy_interaction` | Ambiguous | +| click scanner-like | `undef.scanner_click` | `interaction.scanner_or_bot_interaction` | Ambiguous/automated | +| click unverified | `undef.unverified_click` | `interaction.unverified_actor_interaction` | Identity uncertain | +| reply received | `success.reply_received` | `interaction.reply_received` | Medium-to-strong awareness | +| OOO received | `undef.out_of_office` | `interaction.out_of_office_received` | Mailbox reached, action uncertain | +| complaint | `fail.complaint_received` | `notification.channel.complaint_received` | Strong negative channel signal | +| unsubscribe | `fail.unsubscribed` | `notification.channel.unsubscribe_received` | Future channel constraint | +| suppression added | `fail.suppressed` | `notification.channel.suppression_added` | Channel blocked | + +### 12.2 Coordination Undef Subclasses + +`coordination-engine` may derive these participant uncertainty classes from email evidence: + +```text +undef.pending +undef.technical_acceptance_only +undef.no_signal +undef.weak_positive +undef.identity_uncertain +undef.channel_suspicious +undef.conflicting_evidence +undef.delivery_pending +undef.escalation_required +``` + +Email evidence should commonly produce: + +```text +undef.pending +undef.technical_acceptance_only +undef.weak_positive +undef.identity_uncertain +undef.channel_suspicious +undef.conflicting_evidence +``` + +## 13. Evidence Grading Rules + +### 13.1 Provider Acceptance + +```yaml +event_type: notification.attempt.accepted_by_provider +evidence_grade: + strength: weak + actor_certainty: none + authority_certainty: none + payload_certainty: low + interaction_certainty: none + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: none +notes: + - Provider accepted message for processing. + - Does not prove recipient endpoint acceptance. +``` + +### 13.2 Recipient MX Acceptance + +```yaml +event_type: notification.endpoint.accepted +evidence_grade: + strength: weak + actor_certainty: none + authority_certainty: none + payload_certainty: low + interaction_certainty: none + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: none +notes: + - Recipient mail server accepted the message. + - Does not prove inbox placement or human awareness. +``` + +### 13.3 Hard Bounce + +```yaml +event_type: notification.endpoint.rejected_permanent +evidence_grade: + strength: negative + actor_certainty: none + authority_certainty: none + payload_certainty: none + interaction_certainty: none + timing_certainty: medium + channel_certainty: high + non_repudiation_strength: low +notes: + - Strong evidence the email endpoint is not usable for this attempt. +``` + +### 13.4 Soft Bounce / Deferral + +```yaml +event_type: notification.endpoint.rejected_temporary +evidence_grade: + strength: ambiguous + actor_certainty: none + authority_certainty: none + payload_certainty: none + interaction_certainty: none + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: none +notes: + - Temporary failure or deferral. + - May later resolve or expire. +``` + +### 13.5 Proxy Open + +```yaml +event_type: interaction.proxy_or_privacy_interaction +evidence_grade: + strength: ambiguous + actor_certainty: low + authority_certainty: none + payload_certainty: low + interaction_certainty: low + timing_certainty: low + channel_certainty: medium + non_repudiation_strength: none +notes: + - Open may be caused by privacy proxy, image proxy, or automated prefetch. + - Should not be treated as human awareness in high-assurance scenarios. +``` + +### 13.6 Scanner-Like Click + +```yaml +event_type: interaction.scanner_or_bot_interaction +evidence_grade: + strength: ambiguous + actor_certainty: low + authority_certainty: none + payload_certainty: low + interaction_certainty: low + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: none +notes: + - Click appears automated. + - Should not be treated as recipient engagement. +``` + +### 13.7 Unverified Click + +```yaml +event_type: interaction.unverified_actor_interaction +evidence_grade: + strength: medium + actor_certainty: low + authority_certainty: none + payload_certainty: medium + interaction_certainty: medium + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: none +notes: + - Link was clicked, but actor identity is not proven. + - May indicate awareness but should be confirmed by portal or identity evidence. +``` + +### 13.8 Human Reply + +```yaml +event_type: interaction.reply_received +evidence_grade: + strength: strong + actor_certainty: medium + authority_certainty: low + payload_certainty: medium + interaction_certainty: high + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Reply indicates mailbox-level interaction. + - Identity and authority may still require validation. +``` + +### 13.9 Complaint + +```yaml +event_type: notification.channel.complaint_received +evidence_grade: + strength: negative + actor_certainty: medium + authority_certainty: none + payload_certainty: low + interaction_certainty: high + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Complaint is strong negative channel evidence. + - Future use of the channel may be constrained. +``` + +## 14. Message Timeline API + +`email-connect` SHOULD expose a message timeline suitable for standalone diagnostics and coordination audit. + +```yaml +EmailMessageTimeline: + email_message_id: string + email_attempts: + - email_attempt_id: string + provider_name: string + provider_message_id: string? + events: + - EmailTimelineEvent + current_assessment: EmailEvidenceAssessment +``` + +```yaml +EmailTimelineEvent: + event_id: string + event_type: string + occurred_at: timestamp + source: adapter | provider | inbound_mail | tracking | operator + native_event_type: string? + normalized_event_type: string? + summary: string + evidence_grade: EvidenceGrade + raw_event_ref: string? +``` + +## 15. Adapter Descriptor + +`email-connect` MUST expose an `AdapterDescriptor` compatible with AdapterInterfaceSpecification.md v1.0. + +```yaml +adapter_id: email-connect.default +adapter_name: email-connect +adapter_version: 1.0.0 +adapter_contract_version: 1.0 +adapter_types: + - notification + - communication + - interaction +provider_family: email +provider_name: configurable +deployment_mode: external +supported_channels: + - email +supported_endpoint_types: + - email_address +supported_actions: + - action_type: notification.send + mode: async + idempotency_required: true + - action_type: notification.send_reminder + mode: async + idempotency_required: true + - action_type: notification.register_tracking_context + mode: sync + idempotency_required: true + - action_type: recipient.suppress + mode: sync + idempotency_required: true + - action_type: recipient.unsuppress + mode: sync + idempotency_required: true +emitted_event_types: + - notification.attempt.created + - notification.attempt.accepted_by_adapter + - notification.attempt.rejected_by_adapter + - notification.attempt.accepted_by_provider + - notification.attempt.rejected_by_provider + - notification.attempt.queued + - notification.attempt.delayed + - notification.endpoint.accepted + - notification.endpoint.deferred + - notification.endpoint.rejected_temporary + - notification.endpoint.rejected_permanent + - notification.endpoint.unreachable + - notification.channel.complaint_received + - notification.channel.unsubscribe_received + - notification.channel.suppression_added + - notification.channel.suppression_removed + - notification.channel.reputation_warning + - interaction.notification.opened + - interaction.proxy_or_privacy_interaction + - interaction.link.clicked + - interaction.scanner_or_bot_interaction + - interaction.unverified_actor_interaction + - interaction.reply_received + - interaction.out_of_office_received +evidence_profile: + strongest_evidence_level: weak_to_medium + can_prove_human_awareness: false + can_prove_payload_delivery: false + can_prove_identity: false +identity_profile: + identity_strength: none + authority_strength: none +limitations: + - Recipient mail server acceptance does not prove inbox delivery. + - Absence of bounce does not prove delivery. + - Open tracking is ambiguous. + - Click tracking can be caused by security scanners. + - Email events alone usually cannot prove intended-recipient awareness. + - Email events do not prove payload access unless the payload is embedded and policy accepts that as sufficient. +``` + +## 16. Action Request Handling + +### 16.1 `notification.send` + +`notification.send` sends a new email notification. + +Required fields: + +```text +request_id +action_type +coordination_case_id +participant_id +target_endpoint +content or template_ref +tracking_context +idempotency_key +requested_at +``` + +Example: + +```yaml +request_id: req_001 +action_type: notification.send +coordination_case_id: case_123 +participant_id: participant_456 +channel: email +target_endpoint: + endpoint_type: email_address + value: recipient@example.com +template_ref: secure-document-available +variables: + recipient_name: Example Recipient + portal_link: https://portal.example.com/access/abc +tracking_context: + correlation_id: corr_789 + coordination_case_id: case_123 + participant_id: participant_456 + notification_id: notif_001 + payload_id: payload_777 +idempotency_key: case_123:participant_456:notif_001 +requested_at: 2026-01-01T12:00:00Z +``` + +### 16.2 Action Result + +The adapter returns: + +```yaml +request_id: req_001 +adapter_id: email-connect.default +accepted: true +action_state: accepted +adapter_operation_id: emailop_001 +provider_operation_id: providerop_001 +initial_events: + - event_type: notification.attempt.accepted_by_adapter +received_at: 2026-01-01T12:00:01Z +``` + +The result does not prove the email was delivered or seen. + +## 17. Provider Abstraction + +`email-connect` SHOULD support a provider abstraction layer. + +Provider integration responsibilities: + +* send messages +* store provider message IDs +* verify webhooks +* ingest provider events +* normalize provider event names +* parse bounce reasons +* parse complaints and unsubscribes +* handle provider-specific suppression events +* expose provider health + +Provider model: + +```yaml +EmailProvider: + provider_name: string + provider_account_ref: string + supported_features: + - sending + - webhooks + - bounce_events + - open_tracking + - click_tracking + - suppression_api + - template_rendering + event_mapping_ref: string + configuration_ref: string +``` + +The first implementation MAY use a simulated provider. Real providers SHOULD be added behind this abstraction. + +## 18. Native Provider Event Mapping + +The adapter MUST support provider-specific mapping files or code modules. + +Examples of provider-native event groups: + +```text +accepted +processed +queued +delivered +deferred +delayed +bounce +dropped +failed +rejected +open +click +spam_report +unsubscribe +suppressed +rendering_failed +``` + +Important mapping rule: + +Provider `delivered` events MUST be interpreted carefully. + +For many providers, `delivered` means the receiving mail server accepted the email. It MUST map to: + +```text +notification.endpoint.accepted +``` + +not to coordination success. + +## 19. Bounce Classification + +`email-connect` SHOULD classify bounces into structured reasons. + +```yaml +EmailBounce: + bounce_type: hard | soft | transient | unknown + reason_code: string + enhanced_status_code: string? + smtp_status_code: string? + diagnostic_text: string? + retryable: boolean + classification: + - mailbox_not_found + - domain_not_found + - mailbox_full + - message_too_large + - authentication_failed + - policy_rejected + - content_rejected + - reputation_rejected + - rate_limited + - greylisted + - temporary_server_failure + - suppressed + - unknown +``` + +Suggested mappings: + +| Bounce class | Email assessment | Normalized event | +| ------------------------ | ------------------------------ | -------------------------------------------------------- | +| mailbox_not_found | `fail.mailbox_not_found` | `notification.endpoint.rejected_permanent` | +| domain_not_found | `fail.domain_not_found` | `notification.endpoint.unreachable` | +| mailbox_full | `undef.deferred` | `notification.endpoint.rejected_temporary` | +| authentication_failed | `fail.authentication_rejected` | `notification.endpoint.rejected_permanent` | +| policy_rejected | `fail.policy_rejected` | `notification.endpoint.rejected_permanent` | +| content_rejected | `fail.content_rejected` | `notification.endpoint.rejected_permanent` | +| reputation_rejected | `fail.policy_rejected` | `notification.channel.reputation_warning` plus rejection | +| greylisted | `undef.deferred` | `notification.endpoint.deferred` | +| temporary_server_failure | `undef.deferred` | `notification.endpoint.rejected_temporary` | + +## 20. Suppression Model + +`email-connect` SHOULD maintain or integrate with a suppression model. + +```yaml +EmailSuppression: + endpoint_ref: EndpointRef + suppression_type: hard_bounce | complaint | unsubscribe | manual | provider_policy | unknown + scope: global | tenant | sender_identity | campaign | coordination_case + reason: string? + source: provider | adapter | operator | participant + created_at: timestamp + expires_at: timestamp? +``` + +Suppression should produce evidence: + +```text +notification.channel.suppression_added +notification.channel.suppression_removed +``` + +coordination-engine decides whether suppression means participant failure, channel failure, alternate channel selection, or manual review. + +## 21. Open and Click Tracking Model + +`email-connect` MAY support open and click tracking, but MUST classify such evidence conservatively. + +### 21.1 Open Tracking + +```yaml +EmailOpenEvent: + email_message_id: string + occurred_at: timestamp + ip_address: string? + user_agent: string? + proxy_classification: none | suspected | likely | confirmed + confidence: low | medium | high +``` + +Possible classifications: + +```text +human_like_open +proxy_open +privacy_proxy_open +image_proxy_open +security_scanner_open +unknown_open +``` + +### 21.2 Click Tracking + +```yaml +EmailClickEvent: + email_message_id: string + link_id: string + occurred_at: timestamp + url: string + ip_address: string? + user_agent: string? + scanner_classification: none | suspected | likely | confirmed + confidence: low | medium | high +``` + +Possible classifications: + +```text +human_like_click +scanner_click +link_prefetch +multi_link_scan +unverified_click +unknown_click +``` + +### 21.3 Scanner Detection Hints + +The adapter SHOULD consider: + +* click occurs immediately after delivery +* multiple links clicked within seconds +* HEAD requests or unusual HTTP methods +* known security user agents +* known security vendor IP ranges +* no browser-like behavior +* link fetched but no subsequent portal session +* repeated link checks from data center IPs +* clicks before recipient likely had time to read + +Scanner detection is heuristic and MUST be represented as confidence, not certainty, unless the adapter has strong provider-specific evidence. + +## 22. Address and Endpoint Quality + +`email-connect` SHOULD maintain email endpoint quality signals. + +```yaml +EmailEndpointQuality: + endpoint_ref: EndpointRef + syntax_valid: boolean? + domain_exists: boolean? + mx_exists: boolean? + verified: boolean? + catch_all_suspected: boolean? + role_address_suspected: boolean? + disposable_suspected: boolean? + suppression_state: string? + last_successful_endpoint_acceptance_at: timestamp? + last_hard_bounce_at: timestamp? + last_complaint_at: timestamp? + last_engagement_at: timestamp? +``` + +Endpoint quality may be emitted as diagnostics but should not by itself create coordination success. + +## 23. Channel Health + +`email-connect` SHOULD expose channel health. + +```yaml +EmailChannelHealth: + sender_identity: string + domain: string? + provider_account_ref: string? + status: healthy | degraded | failing | unknown + authentication_status: + spf: pass | fail | unknown + dkim: pass | fail | unknown + dmarc: pass | fail | unknown + reputation_status: good | warning | poor | unknown + bounce_rate: number? + complaint_rate: number? + deferral_rate: number? + provider_degradation: + - string +``` + +Health-related normalized events: + +```text +notification.channel.reputation_warning +system.provider.degraded +system.provider.unavailable +system.adapter.health_changed +``` + +## 24. Security Requirements + +`email-connect` MUST: + +* protect provider credentials +* verify provider webhook signatures where available +* validate inbound webhook source authenticity where possible +* avoid logging sensitive message content by default +* preserve idempotency +* prevent duplicate sends for repeated idempotency keys +* support tenant or sender separation where applicable +* avoid leaking tracking tokens +* protect suppression lists +* sanitize inbound replies and auto-replies + +## 25. Privacy Requirements + +`email-connect` SHOULD: + +* store message content only when necessary +* support metadata-only mode +* support raw event redaction +* support configurable retention of raw provider events +* store endpoint references instead of endpoint values where possible +* avoid storing unnecessary open/click details if not needed +* allow tracking to be disabled +* separate operational diagnostics from coordination evidence +* support deletion or anonymization workflows where applicable + +## 26. Reliability Requirements + +`email-connect` MUST support: + +* idempotent send requests +* duplicate webhook event detection +* out-of-order event handling +* late bounce handling +* retryable provider failures +* non-retryable provider failures +* provider timeout handling +* correlation preservation +* dead-letter handling for unprocessable events + +Late events MUST be preserved. + +Example: + +```text +MX accepted at 10:00 +participant unresolved at 14:00 +hard bounce arrives at 18:00 +``` + +The hard bounce must still be recorded and emitted as evidence. + +## 27. Raw Event Preservation + +`email-connect` SHOULD preserve raw provider events or references to them. + +```yaml +RawEmailEventRef: + raw_event_id: string + provider_name: string + storage_ref: string? + received_at: timestamp + redacted: boolean +``` + +Normalized events should reference raw event data where available: + +```yaml +raw_event_ref: raw_email_event_123 +``` + +## 28. Minimal API Surface + +`email-connect` SHOULD expose a headless API. + +### 28.1 Adapter Contract API + +Required conceptual operations: + +```text +GET /adapter/descriptor +GET /adapter/health +POST /adapter/actions +POST /adapter/events/provider +GET /adapter/events +GET /adapter/messages/{id}/timeline +GET /adapter/messages/{id}/assessment +``` + +The actual transport may differ, but these conceptual operations should exist. + +### 28.2 Standalone API + +Useful standalone operations: + +```text +POST /email/send +GET /email/messages/{id} +GET /email/messages/{id}/timeline +GET /email/messages/{id}/assessment +GET /email/endpoints/{id}/quality +POST /email/suppressions +DELETE /email/suppressions/{id} +GET /email/channel-health +``` + +## 29. Example End-to-End Flow + +### 29.1 Secure Document Notification + +1. `coordination-engine` creates a coordination case. +2. `portal-connect` creates an authenticated document access link. +3. `coordination-engine` sends `notification.send` to `email-connect`. +4. `email-connect` renders and dispatches email. +5. Provider accepts the message. +6. `email-connect` emits `notification.attempt.accepted_by_provider`. +7. Recipient MX accepts message. +8. `email-connect` emits `notification.endpoint.accepted`. +9. User clicks the link. +10. `email-connect` emits `interaction.unverified_actor_interaction`. +11. User logs into portal. +12. `portal-connect` emits `identity.actor_authenticated`. +13. User downloads document. +14. `portal-connect` emits `delivery.payload.downloaded`. +15. `coordination-engine` marks participant complete. + +Email contributed useful path evidence but did not by itself prove the result. + +### 29.2 Hard Bounce and Fallback + +1. `email-connect` sends email. +2. Provider accepts the message. +3. Recipient server returns hard bounce. +4. `email-connect` emits `notification.endpoint.rejected_permanent`. +5. `coordination-engine` marks email channel failed for that participant. +6. Policy engine selects SMS fallback or manual review. + +### 29.3 Scanner Click + +1. Email is accepted by MX. +2. Link is clicked immediately by a known scanner pattern. +3. `email-connect` emits `interaction.scanner_or_bot_interaction`. +4. `coordination-engine` keeps participant in `undef.identity_uncertain`. +5. No reminder is suppressed merely because of the scanner click. +6. If no portal evidence appears after threshold, reminder or alternate channel is triggered. + +## 30. Provider Implementation Guidance + +The first real provider integration SHOULD be selected based on: + +* webhook support +* bounce event quality +* provider metadata/correlation support +* suppression API +* event reliability +* local availability and cost +* compatibility with transactional email +* ability to separate message streams + +The implementation SHOULD avoid hardcoding provider semantics into the core email model. + +Provider-specific modules should map to the email-native model first, then to normalized coordination events. + +```text +Provider event +→ email-native event +→ EmailEvidenceAssessment +→ EvidenceEvent for coordination-engine +``` + +## 31. Message Steparation + +`email-connect` SHOULD support sender identities or message streams. + +Recommended streams: + +```text +transactional +notification +legal_or_high_assurance_notice +marketing +system_alert +test +``` + +High-assurance or legally relevant notifications SHOULD NOT share reputation-critical infrastructure with marketing traffic unless explicitly accepted by policy. + +Stream separation may affect: + +* sender domain +* return path +* DKIM identity +* provider account +* suppression scope +* complaint handling +* deliverability diagnostics + +## 32. Legal and Compliance Disclaimer + +`email-connect` does not by itself provide legal proof of delivery, legal notice, acceptance, signature, or contract closure. + +It provides evidence from the email channel. + +Scenario-specific applications and `coordination-engine` policies may combine email evidence with stronger evidence from portal, identity, signature, payment, archive, or manual processes. + +The adapter MUST avoid naming technical email events in ways that imply legal success. + +For example: + +Use: + +```text +notification.endpoint.accepted +``` + +Avoid: + +```text +recipient_notified +legal_delivery_completed +``` + +## 33. MVP Scope + +The first useful version of `email-connect` should implement: + +1. Adapter descriptor. +2. Adapter health endpoint. +3. `notification.send`. +4. Idempotent send request handling. +5. Simulated provider or one real provider. +6. Email message and attempt records. +7. Provider event ingestion. +8. Basic bounce classification. +9. Basic open/click classification. +10. Evidence event generation. +11. Message timeline. +12. Message assessment. +13. Suppression support. +14. Mapping to AdapterInterfaceSpecification.md v1.0. + +### MVP Required Email Events + +```text +notification.attempt.accepted_by_adapter +notification.attempt.rejected_by_adapter +notification.attempt.accepted_by_provider +notification.attempt.rejected_by_provider +notification.endpoint.accepted +notification.endpoint.deferred +notification.endpoint.rejected_temporary +notification.endpoint.rejected_permanent +interaction.proxy_or_privacy_interaction +interaction.scanner_or_bot_interaction +interaction.unverified_actor_interaction +notification.channel.complaint_received +notification.channel.suppression_added +``` + +### MVP Acceptance Criteria + +The MVP is acceptable when it can: + +1. Accept a coordination-compatible send request. +2. Dispatch or simulate an email. +3. Preserve correlation and idempotency. +4. Ingest or simulate provider events. +5. Produce normalized evidence events. +6. Classify MX acceptance as weak technical evidence. +7. Classify hard bounce as strong failure evidence. +8. Classify proxy opens and scanner clicks as ambiguous. +9. Provide a message timeline. +10. Provide an email evidence assessment. +11. Integrate with coordination-engine without overclaiming success. + +## 34. Future Extensions + +Potential future capabilities: + +* multi-provider routing +* provider failover +* domain reputation monitoring +* inbox placement seed testing +* DMARC aggregate report ingestion +* BIMI diagnostics +* advanced bounce parsing +* advanced scanner detection +* inbound reply classification +* natural-language reply intent extraction +* tenant-specific suppression scopes +* message stream isolation +* deliverability analytics +* adaptive sending policies +* integration with archive systems +* S/MIME or PGP support +* signed email support +* email authentication diagnostics +* AI-assisted deliverability analysis + +## 35. Non-Goals + +`email-connect` is not: + +* a marketing automation platform +* a newsletter campaign manager +* a CRM +* a full workflow engine +* a legal delivery service by itself +* a document portal +* a payment system +* a signature system +* the owner of coordination case success + +It may integrate with such systems or be used by them. + +## 36. Summary + +`email-connect` models email as a useful but uncertain communication and notification channel. + +Its job is to: + +* send emails +* ingest provider events +* classify email outcomes +* normalize email evidence +* preserve message timelines +* expose endpoint and channel diagnostics +* integrate cleanly with `coordination-engine` + +The key rule is: + +> Email events are evidence, not result satisfaction. email-connect reports email-channel facts and uncertainty. coordination-engine evaluates intended results. + diff --git a/spec/HybridMailProviderApiComparison.md b/spec/HybridMailProviderApiComparison.md new file mode 100644 index 0000000..7979fb2 --- /dev/null +++ b/spec/HybridMailProviderApiComparison.md @@ -0,0 +1,925 @@ +# HybridMailProviderApiComparison.md + +## 1. Document Status + +**Document:** HybridMailProviderApiComparison.md +**Project:** hybridmail-connect +**Related Specifications:** + +* `HybridmailAdapterSpecification.md` +* `HybridmailBinectSpecification.md` +* `HybridmailPingenSpecification.md` +* `HybridmailDpagSpecification.md` +* `AdapterInterfaceSpecification.md` + +**Status:** Draft v1.0 +**Scope:** Provider API comparison, SWOT-style assessment, implementation implications, and coding-agent friendliness. + +## 2. Purpose + +This document compares three hybrid-mail provider APIs and provider-flavor candidates for `hybridmail-connect`: + +* Binect +* Pingen +* Deutsche Post / E-POSTBUSINESS API / DPAG Hybrid Mail Shipments E-POST + +The comparison focuses on their suitability as provider flavors for a generalized `hybridmail-connect` adapter that integrates with `coordination-engine`. + +The goal is not to pick a single winner, but to understand: + +* common baseline capabilities +* provider-specific strengths +* provider-specific risks and gaps +* evidence-model implications +* adapter implementation complexity +* coding-agent friendliness +* how public API digestability could be improved + +## 3. Executive Summary + +All three providers support the core hybrid-mail pattern: + +```text +digital document submission +→ provider-side validation / preparation +→ physical production +→ postal dispatch +→ status or delivery/return evidence where available +``` + +However, they differ in emphasis: + +| Provider | Strongest apparent API/product emphasis | +| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Binect** | Document-centric business-letter workflow with upload, attachments, preview, validation, sending, and status queries. | +| **Pingen** | Developer-friendly letter API with SDKs, idempotency, Track & Trace, webhooks, staging, send limits, and return-mail processing. | +| **DPAG / E-POSTBUSINESS** | Deutsche Post-native hybrid-mail infrastructure with PDF/PDF-A shipment submission, bulk upload, production data, shipment tracking, registered-mail use cases, positioning aids, cover sheets, and scheduled dispatch. | + +For `hybridmail-connect`, the best baseline design is a superset model with provider-specific capability descriptors. No provider flavor should be treated as the normative model. Instead, each provider should map into the generic lifecycle: + +```text +uploaded +accepted +validated +ready +submitted +processing +production_started +production_completed +postal_handover +in_transit +delivery_confirmed +undeliverable +return_received +status_unknown +``` + +The most important evidence principle remains: + +> Ordinary hybrid-mail APIs usually provide strong evidence of digital submission, validation, production, or postal dispatch. They do not automatically prove human awareness or legal receipt. + +## 4. Source Basis and Confidence + +This comparison is based on public provider information and public developer/API materials. + +### Binect + +Public Binect API information confirms a REST/Swagger API for business-letter dispatch, document upload, attachments, print/shipping options, preview, sending, document status, shipping status, validation of address position/address format/restricted areas, and full-service print/enveloping/franking/dispatch. The public Swagger page exposes Binect API concepts and validation descriptions, while the Binect API page positions it as an integration API for document dispatch. + +### Pingen + +Pingen publicly emphasizes a developer-friendly Letter API with SDKs for PHP, Python, Go, and .NET, Idempotency-Key support, rate limiting, configurable send limits, staging/sandbox, Track & Trace, webhooks, document validation, and return-mail processing. Pingen also documents detailed API status information and tracking “from A to Z.” + +### DPAG / E-POSTBUSINESS + +Deutsche Post’s Hybrid Mail Shipments E-POST API documentation states that the E-POSTBUSINESS API expects single PDF documents for mail shipments, supports one-by-one and bulk upload, and provides detailed shipment processing information and bulk status requests. Deutsche Post’s business API material describes REST/Swagger documentation, shipment-level status with production data and shipment tracking, individual-to-mass submission, address-window positioning aids, automatic positioning, individual cover sheets, scheduled dispatch, and hybrid dispatch services. + +### Confidence Notes + +The high-level comparison is reliable. Exact endpoint names, exact status names, native enum values, webhook payload shapes, product availability, account-specific capabilities, and cancellation behavior require validation against the live provider API documentation and target account configuration. + +## 5. Common Baseline Across Providers + +The shared baseline for `hybridmail-connect` should include: + +```text +document / PDF payload submission +provider-side acceptance +document validation +address / layout suitability checks +print and shipping options +sending / submission step +status polling or status retrieval +production / processing status +postal dispatch or handover evidence +return / undeliverable evidence where available +batch or serial processing where available +provider-specific status preservation +``` + +## 6. Core Differences at a Glance + +| Dimension | Binect | Pingen | DPAG / E-POSTBUSINESS | +| ---------------------------- | -------------------------------------------------- | -------------------------------------- | ------------------------------------------------------- | +| API style | REST/Swagger | Developer API with SDKs and docs | REST/Swagger API | +| Primary model | Document + sending | Letter + Track & Trace | PDF shipment / mail shipment | +| Public developer positioning | Business software / DMS / CRM integration | Strong developer friendliness | Partner/business software integration | +| Upload | Document upload | Letter/file creation | PDF shipment upload | +| Bulk / serial | Supported / relevant | Supported / relevant | One-by-one and bulk upload | +| Attachments | Publicly documented | Provider-specific | Provider-specific | +| Preview | Publicly documented | Likely / provider-specific | Provider-specific | +| Validation | Address position, address format, restricted areas | Document/letter validation | PDF/PDF-A, address-window, positioning, product/layout | +| Idempotency | Adapter-managed unless API support confirmed | Public Idempotency-Key support | Adapter-managed unless API support confirmed | +| Webhooks | Provider-specific / unclear publicly | Publicly documented webhook categories | Provider-specific / unclear publicly | +| Track & Trace | Status APIs | Strong public emphasis | Publicly documented shipment tracking / production data | +| Return mail | Provider-specific | Strong public emphasis | Product/provider-specific | +| Registered mail | Provider-specific | Supported/product-dependent | Publicly referenced | +| Delivery confirmation | Product-specific | Product-specific | Product-specific | +| Scheduled dispatch | Provider-specific | Provider-specific | Publicly referenced | +| Address positioning | Validation-oriented | Configurable address position | Positioning aids / automatic positioning | +| Cover sheets | Not public baseline | Provider-specific | Publicly referenced | +| Coding-agent friendliness | Medium | High | Medium | + +## 7. Provider-Specific Assessment: Binect + +## 7.1 Binect Profile + +Binect appears best modeled as a **document-centric hybrid-mail provider**. + +The workflow centers on: + +```text +document upload +→ validation +→ optional attachments +→ print/shipping options +→ preview +→ sending +→ document/shipping status +``` + +Binect’s public API/Swagger documentation strongly supports the concept of document validation, including address-position, address-format, and restricted-area checks. It also supports attachments and preview retrieval, which makes it attractive for controlled document-dispatch workflows. + +## 7.2 Binect Strengths + +* Strong alignment with business-document dispatch. +* Public Swagger/API presence. +* Clear document upload and sending workflow. +* Public evidence of attachment handling. +* Public evidence of preview support. +* Validation concerns are visible and practically relevant. +* Suitable for DMS, CRM, and business-software integration. +* Likely a strong fit for Binect-owned or Binect-adjacent coordination scenarios. + +## 7.3 Binect Weaknesses + +* Public documentation is less marketing-developer-oriented than Pingen. +* Publicly available details about webhooks, idempotency, return mail, registered products, and delivery confirmation are less explicit. +* Exact native status model must be extracted from the live Swagger/API. +* Some concepts may require account-specific or product-specific configuration. +* Coding agents may need additional curated examples and mapping tables to avoid guessing. + +## 7.4 Binect Opportunities + +* Strong reference implementation for a German business-letter workflow. +* Good fit for `coordination-engine` scenarios involving document validation before dispatch. +* Attachments and preview support can support robust approval/review loops. +* Validation output can feed a `postal-address-quality` or `document-layout-linting` subsystem. +* Could become the first practical provider flavor if the user has strong domain access and Binect-specific knowledge. + +## 7.5 Binect Threats / Risks + +* If exact status semantics are not well documented, agents may over-map provider statuses to strong evidence. +* If idempotency is not native, duplicate physical sends must be prevented by the adapter. +* If webhooks are missing or not available, polling complexity increases. +* If return-mail and registered-letter semantics are not available through API, evidence strength remains limited for high-assurance scenarios. +* Provider-specific assumptions could leak into the generic `HybridmailAdapterSpecification.md` if not isolated. + +## 7.6 Binect Implementation Priority + +Recommended implementation priority: + +```text +1. Document upload +2. Validation mapping +3. Attachment upload +4. Print/shipping options +5. Preview retrieval +6. Send/submission +7. Status polling +8. Child/serial document status +9. Return/undeliverable mapping if available +10. Registered/delivery-confirmation mapping if available +``` + +## 8. Provider-Specific Assessment: Pingen + +## 8.1 Pingen Profile + +Pingen appears best modeled as a **developer-friendly letter API** with strong operational and integration features. + +The workflow centers on: + +```text +letter/file creation +→ validation +→ auto-send or manual send +→ Track & Trace +→ webhooks +→ return-mail / undeliverable / delivered status categories +``` + +Pingen publicly emphasizes SDKs, Idempotency-Key headers, rate limiting, configurable send limits, staging/sandbox, Track & Trace, and webhooks. This makes it the most coding-agent-friendly provider in the comparison. + +## 8.2 Pingen Strengths + +* Strong developer positioning. +* Multi-language SDKs. +* Public idempotency support. +* Public rate-limit and send-limit support. +* Public staging/sandbox or full-feature simulation. +* Public Track & Trace emphasis. +* Public webhook categories. +* Strong return-mail processing story. +* Clear API-first product narrative. +* Excellent candidate for proving the generic `hybridmail-connect` adapter model. + +## 8.3 Pingen Weaknesses + +* Strong developer story does not remove the need to verify exact webhook payload semantics. +* “Delivered” terminology must be interpreted carefully and product-dependently. +* Some postal delivery confirmation semantics depend on selected products. +* Return-mail evidence can arrive late, requiring robust event-sourcing. +* Address-position and validation mapping must still be carefully normalized. + +## 8.4 Pingen Opportunities + +* Best candidate for an external reference provider flavor. +* Useful for validating webhook-first and polling-fallback architecture. +* Idempotency support can become the model for duplicate-send prevention. +* Track & Trace can help define the hybrid-mail timeline API. +* Staging/sandbox can support automated integration testing and coding-agent test loops. +* Return-mail processing can help define the negative-evidence model. + +## 8.5 Pingen Threats / Risks + +* Overinterpreting Track & Trace events as delivery confirmation. +* Treating “sent” as delivered. +* Treating product-specific delivered-letter events as generally available. +* Assuming webhook categories equal exact coordination event semantics without detailed payload mapping. +* Depending too heavily on SDK behavior rather than the underlying API contract. + +## 8.6 Pingen Implementation Priority + +Recommended implementation priority: + +```text +1. Authentication and workspace/account setup +2. Idempotent letter creation +3. Validation mapping +4. Auto-send/manual-send handling +5. Track & Trace polling +6. Webhook ingestion +7. Return-mail / undeliverable mapping +8. Delivered-letter mapping, product-dependent +9. Rate-limit/send-limit handling +10. Sandbox test harness +``` + +## 9. Provider-Specific Assessment: DPAG / E-POSTBUSINESS + +## 9.1 DPAG Profile + +DPAG / E-POSTBUSINESS appears best modeled as a **postal-infrastructure-native hybrid-mail shipment API**. + +The workflow centers on: + +```text +PDF/PDF-A shipment upload +→ validation / positioning / cover-sheet handling +→ single or bulk submission +→ production data +→ shipment tracking +→ registered-mail / delivery-confirmation product evidence +``` + +The public Deutsche Post developer material explicitly describes single PDF documents, one-by-one and bulk upload, shipment processing information, bulk status requests, and tracking from uploading through delivery. Deutsche Post business API materials emphasize REST/Swagger, production data, shipment tracking, mass submission, address-window positioning aids, automatic positioning, individual cover sheets, and scheduled dispatch. + +## 9.2 DPAG Strengths + +* Direct connection to Deutsche Post hybrid-mail infrastructure. +* Strong postal-product alignment. +* Publicly documented PDF/PDF-A focus. +* Publicly documented bulk upload and bulk status. +* Publicly documented production data and shipment tracking. +* Publicly documented address-window positioning aids. +* Publicly documented automatic positioning and individual cover sheets. +* Publicly documented scheduled dispatch. +* Publicly referenced registered-letter use cases. +* Strong fit for German enterprise postal-delivery scenarios. + +## 9.3 DPAG Weaknesses + +* Adoption model appears more partner/business-software oriented. +* Exact active API access may require activation and credentials. +* Public material is less direct as a self-service developer experience than Pingen. +* Exact native status names and payloads need live API documentation. +* Exact idempotency behavior is unclear from public search result material. +* Exact webhook behavior is unclear from public search result material. +* Provider semantics may be more complex due to product diversity. + +## 9.4 DPAG Opportunities + +* Best candidate for postal-product completeness in the German market. +* Strong candidate for high-assurance physical dispatch scenarios. +* Registered-mail and shipment-tracking support can strengthen evidence models. +* Bulk upload/status can validate large-scale coordination cases. +* Address positioning and cover-sheet features can inform a powerful `print-layout-validation` subsystem. +* Scheduled dispatch can support deadline-aware coordination policies. + +## 9.5 DPAG Threats / Risks + +* Integration may be harder without partner activation or live account access. +* Coding agents may struggle if documentation is distributed across Swagger, PDFs, FAQs, and partner docs. +* Native status terminology may tempt overmapping to `delivery_confirmed`. +* Bulk status may be misread as per-recipient success unless child statuses are modeled. +* If idempotency is not native, duplicate physical shipment prevention must be adapter-managed. +* Product-specific registered-letter semantics must not be generalized. + +## 9.6 DPAG Implementation Priority + +Recommended implementation priority: + +```text +1. Authentication / activation model +2. Single PDF/PDF-A shipment upload +3. Validation and PDF/PDF-A issue mapping +4. Status polling +5. Production data mapping +6. Postal handover mapping +7. Bulk upload and bulk status +8. Address positioning / automatic positioning metadata +9. Cover-sheet support +10. Scheduled dispatch support +11. Registered-mail / delivery-confirmation mapping +``` + +## 10. SWOT Summary Matrix + +## 10.1 Binect SWOT + +| Category | Assessment | +| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| Strengths | Document-centric workflow, attachments, preview, validation, business-software fit, Swagger/API visibility. | +| Weaknesses | Less explicit public detail on webhooks, idempotency, return mail, registered/delivery-confirmation semantics. | +| Opportunities | Strong Binect-specific implementation candidate, document validation as differentiator, good fit for DMS/CRM dispatch. | +| Threats | Risk of status overinterpretation, duplicate-send risk if no native idempotency, polling complexity, account-specific capability uncertainty. | + +## 10.2 Pingen SWOT + +| Category | Assessment | +| ------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| Strengths | Developer-friendly, SDKs, Idempotency-Key, rate limits, send limits, staging, Track & Trace, webhooks, return-mail processing. | +| Weaknesses | Exact delivered/return webhook semantics still require careful mapping; delivery confirmation is product-dependent. | +| Opportunities | Best external reference implementation, strong testability, strong coding-agent friendliness, good event-driven model. | +| Threats | Overinterpreting Track & Trace or delivered labels, relying too much on SDK abstraction, late return events changing assessments. | + +## 10.3 DPAG SWOT + +| Category | Assessment | +| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Strengths | Postal-infrastructure-native, PDF/PDF-A focus, bulk upload/status, production data, shipment tracking, address positioning, cover sheets, scheduled dispatch, registered-mail use cases. | +| Weaknesses | More partner/account activation complexity, less self-service developer digestability, exact webhook/idempotency details unclear publicly. | +| Opportunities | Strongest postal-product depth, excellent for German enterprise and high-assurance physical dispatch, bulk and registered-mail patterns. | +| Threats | Documentation fragmentation, product-specific complexity, risk of overmapping status, harder test setup without active account. | + +## 11. Evidence Model Comparison + +| Evidence stage | Binect | Pingen | DPAG | +| -------------------------------------- | ------------------------- | ------------------------------------- | ---------------------------------------------- | +| Digital upload accepted | Yes | Yes | Yes | +| Validation passed/failed | Strong public evidence | Strong public evidence | Strong public evidence | +| Preview/review | Publicly visible | Provider-specific / likely | Provider-specific | +| Submitted for sending | Yes | Yes | Yes | +| Production status | Status-dependent | Track & Trace-dependent | Publicly emphasized production data | +| Postal handover | Status-dependent | Sent-letter / Track & Trace semantics | Shipment tracking / status semantics | +| Ordinary delivery confirmation | Not generally assumed | Not generally assumed | Not generally assumed | +| Product-specific delivery confirmation | Provider/account-specific | Product-dependent | Product-dependent / registered products | +| Return/undeliverable evidence | Provider-specific | Strong public feature | Product/provider-specific | +| Address correction evidence | Provider-specific | Provider-specific | Premiumadress / product-specific possibilities | + +## 12. Superset Baseline for hybridmail-connect + +The provider comparison supports the following superset baseline: + +```text +supports_document_upload +supports_validation +supports_address_validation +supports_address_position_validation +supports_restricted_area_validation +supports_pdf_or_pdfa_validation +supports_print_options +supports_shipping_options +supports_preview +supports_single_letter +supports_bulk_or_serial_letters +supports_send_submission +supports_status_polling +supports_webhooks +supports_track_and_trace +supports_production_status +supports_postal_handover_status +supports_registered_mail +supports_delivery_confirmation +supports_return_mail +supports_address_correction +supports_scheduled_dispatch +supports_cover_sheet +supports_idempotency +supports_staging +supports_rate_limits +supports_send_limits +``` + +No flavor supports all of these equally. Therefore every provider flavor must declare capabilities explicitly. + +## 13. Recommended Provider Flavor Strategy + +## 13.1 Use Pingen as External Developer-Experience Reference + +Pingen is the cleanest reference for: + +```text +idempotency +webhooks +Track & Trace +staging/sandbox +SDK examples +return-mail events +developer-facing onboarding +``` + +Pingen is likely the best provider to test the generic adapter event model from the outside because its public developer narrative is strongest. + +## 13.2 Use Binect as Business-Document Workflow Reference + +Binect is the best reference for: + +```text +document upload +attachments +print/shipping options +preview +business-software integration +address/layout validation +``` + +Binect is strategically important if `coordination-engine` is intended to support Binect-related products or partner scenarios. + +## 13.3 Use DPAG as Postal-Depth Reference + +DPAG is the best reference for: + +```text +postal-product richness +production data +shipment tracking +bulk upload/status +registered-mail variants +address positioning aids +automatic positioning +cover sheets +scheduled dispatch +German postal infrastructure alignment +``` + +DPAG is the strongest conceptual source for a complete German hybrid-mail standard, even if implementation may require more account and partner setup. + +## 14. Implementation Complexity Assessment + +| Provider | Implementation complexity | Reason | +| -------- | ------------------------: | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Pingen | Medium | Strong docs/SDKs/idempotency/webhooks reduce integration complexity, but status semantics still require careful evidence mapping. | +| Binect | Medium to High | Workflow is clear but exact status/webhook/idempotency semantics likely need live Swagger/API and domain knowledge. | +| DPAG | High | Rich product set, partner activation, PDF/PDF-A, positioning, production data, tracking, bulk status, and product-specific semantics increase complexity. | + +## 15. Coding-Agent Friendliness + +## 15.1 What Makes an API Coding-Agent Friendly? + +An API is coding-agent friendly when a coding agent can reliably infer and implement correct behavior from its documentation without relying on undocumented assumptions. + +Key properties: + +```text +machine-readable OpenAPI schema +stable endpoint semantics +complete enum documentation +clear lifecycle diagrams +exact state machine definitions +idempotency semantics +webhook schemas +error-code catalog +retry rules +sandbox/test environment +sample requests and responses +SDKs +typed schemas +provider-specific glossary +status-to-business-meaning warnings +capability descriptor +contract tests +``` + +## 15.2 Coding-Agent Friendliness Comparison + +| Dimension | Binect | Pingen | DPAG | +| ------------------------------------------------------ | ----------------------- | ---------------------- | ---------------------------------------- | +| Public developer positioning | Medium | High | Medium | +| Public Swagger/OpenAPI visibility | High | High / likely via docs | High | +| SDK availability | Not emphasized publicly | Strongly emphasized | Not primary public emphasis | +| Idempotency clarity | Unclear publicly | Strong | Unclear publicly | +| Webhook clarity | Unclear publicly | Strong | Unclear publicly | +| Sandbox/staging clarity | Unclear publicly | Strong | Present but activation/account-dependent | +| Status semantics digestability | Medium | Medium-High | Medium | +| Product semantics complexity | Medium | Medium | High | +| Agent implementation confidence from public docs alone | Medium | High | Medium-Low | +| Need for curated flavor spec | High | Medium | Very High | + +## 15.3 Pingen Coding-Agent Friendliness + +Pingen is most coding-agent friendly because it publicly emphasizes: + +* SDKs +* code examples +* developer-friendly documentation +* Idempotency-Key support +* rate limits +* send limits +* staging/sandbox +* Track & Trace +* webhooks + +These are exactly the affordances coding agents need to produce reliable integrations. + +Remaining improvements would include: + +```text +machine-readable event lifecycle schema +canonical webhook payload examples +explicit status-to-evidence mapping +clear statement of what "delivered" means per product +typed error-code catalog +contract-test suite +``` + +## 15.4 Binect Coding-Agent Friendliness + +Binect has a public Swagger/API entry point and clearly relevant document workflow concepts. This is good for agents because the API is inspectable and operation-oriented. + +However, coding-agent friendliness would improve significantly with: + +```text +explicit OpenAPI examples for every endpoint +published status enum table +published validation issue enum table +published webhook contract if webhooks exist +published idempotency policy +provider status lifecycle diagram +duplicate-send prevention guide +preview and attachment examples +serial/child document examples +``` + +Binect’s public documentation is enough for a strong conceptual provider flavor, but a coding agent implementing the adapter would benefit from curated mapping files generated from the live Swagger. + +## 15.5 DPAG Coding-Agent Friendliness + +DPAG has strong conceptual and product depth, and public material confirms REST/Swagger documentation and many important capabilities. However, the integration model appears more partner/account-activation oriented, and the relevant knowledge is spread across developer pages, business API pages, PDFs, FAQs, partner docs, and possibly active-account Swagger. + +Coding-agent friendliness would improve significantly with: + +```text +single canonical OpenAPI bundle +machine-readable product capability matrix +complete status lifecycle map +PDF/PDF-A validation issue catalog +address-window positioning examples +automatic positioning examples +cover-sheet examples +bulk status examples +registered-mail event examples +delivery-confirmation semantics table +idempotency guidance +sandbox recipes +agent-readable "do not overclaim delivery" notes +``` + +DPAG is powerful but likely needs the most curation before coding agents can implement safely. + +## 16. How API Digestability Could Be Improved + +## 16.1 Provide Capability Descriptors + +Every provider should expose a machine-readable capability descriptor. + +Example: + +```yaml +provider: pingen +supports: + webhooks: true + idempotency: true + return_mail: true + delivery_confirmation: product_dependent + registered_mail: true + preview: true + bulk: true + staging: true +``` + +This avoids agent guesswork and makes provider flavor selection automatable. + +## 16.2 Publish Explicit Lifecycle State Machines + +Provider documentation should include lifecycle diagrams with exact state names. + +Example: + +```text +created +→ uploaded +→ validation_pending +→ validation_passed +→ ready_for_sending +→ submitted +→ processing +→ handed_to_post +→ delivered | returned | unknown +``` + +Each state should declare: + +```text +meaning +terminal or non-terminal +retry behavior +whether it implies postal handover +whether it implies delivery confirmation +whether it is product-dependent +``` + +## 16.3 Separate Production, Postal Handover, and Delivery Confirmation + +Hybrid-mail APIs should explicitly separate: + +```text +production_completed +postal_handover +postal_in_transit +delivery_confirmed +return_received +``` + +This is essential for coordination-engine because these states have different evidence grades. + +## 16.4 Provide Status-to-Evidence Warnings + +Each provider should document warnings such as: + +```text +"sent" means the letter entered the sending process, not that the recipient received it. +"handed over" means the postal carrier accepted the letter, not that the recipient read it. +"delivered" is only available for selected products and does not imply human awareness. +``` + +These warnings are critical for safe automation. + +## 16.5 Publish Webhook Schemas and Examples + +Webhook documentation should include: + +```text +event name +payload schema +example payload +retry behavior +signature verification +idempotency/deduplication key +event ordering assumptions +late event behavior +test-event endpoint +``` + +Pingen appears strongest here in public positioning, but all providers would benefit from standardized webhook docs. + +## 16.6 Provide Idempotency Semantics + +For hybrid mail, idempotency is not a convenience. It is a safety requirement. + +Every provider should document: + +```text +which endpoints support idempotency keys +how long keys are retained +what constitutes a conflict +what happens after timeout +how duplicate sends are prevented +whether idempotency covers upload, send, or both +``` + +If provider-native idempotency is unavailable, adapter-managed idempotency must compensate. + +## 16.7 Provide Validation Issue Catalogs + +Validation errors should be machine-readable. + +Useful fields: + +```text +issue_code +severity +category +page_number +bounding_box +fixable +human_message +machine_recommendation +``` + +This would greatly improve automated correction workflows. + +## 16.8 Provide Sandbox and Golden Test Cases + +Coding agents need reproducible fixtures. + +Recommended test fixtures: + +```text +valid one-page domestic letter +valid multi-page duplex letter +invalid address position +restricted-area violation +invalid PDF +PDF/A issue +unsupported country +return-mail simulation +registered-mail delivered simulation +bulk partial-failure simulation +duplicate idempotency simulation +``` + +These would make adapter implementation much safer. + +## 16.9 Provide OpenAPI Files Optimized for Code Generation + +Provider OpenAPI documents should be: + +```text +complete +versioned +downloadable +validated +example-rich +schema-rich +enum-rich +error-rich +webhook-inclusive +security-scheme-explicit +``` + +A public Swagger UI is helpful, but agents benefit more from a downloadable OpenAPI file plus examples and contract tests. + +## 17. Recommended Agent-Readable Provider Profile Format + +Each provider flavor should maintain an agent-readable profile file. + +Example: + +```yaml +provider: pingen +flavor_version: 1.0 +contract_version: 1.0 +confidence: public_docs_plus_live_api_required +capabilities: + idempotency: true + webhooks: true + track_and_trace: true + return_mail: true + delivery_confirmation: product_dependent +status_mapping: + sent: + normalized_event: delivery.postal.handed_over + confidence: medium + warning: "Verify native semantics before enabling this mapping." + delivered: + normalized_event: delivery.postal.delivery_confirmed + confidence: product_dependent + warning: "Only for delivery-confirmation-capable product." +required_tests: + - duplicate_send_prevention + - validation_failure_mapping + - return_mail_late_event + - delivered_event_product_semantics +``` + +This would allow coding agents to implement providers without repeatedly re-reading large documents. + +## 18. Recommended Implementation Order + +## 18.1 For Proving the Generic Adapter + +Use Pingen first if external provider access is easy. + +Reason: + +```text +idempotency + webhooks + Track & Trace + staging + SDKs +``` + +make it a strong proving ground. + +## 18.2 For Binect Strategic Fit + +Use Binect first if the immediate business case is Binect-owned or Binect-integrated. + +Reason: + +```text +document upload + attachments + preview + validation + business-letter dispatch +``` + +aligns strongly with Binect scenarios. + +## 18.3 For Postal Completeness + +Use DPAG early for conceptual completeness, but probably not as the first implementation unless account access and documentation are ready. + +Reason: + +```text +product richness + bulk + production data + tracking + registered mail +``` + +make it strategically important but implementation-heavy. + +## 19. Provider Flavor Risk Controls + +Each provider flavor MUST include the following safety controls: + +```text +adapter-managed idempotency, even if provider idempotency exists +raw status preservation +conservative event mapping +product-specific delivery-confirmation mapping +batch child-status mapping +late return-event handling +explicit evidence grading +duplicate physical-send prevention +manual override support +provider capability descriptor +``` + +## 20. Recommended Hybrid-Mail Provider Abstraction + +The comparison supports this provider abstraction: + +```text +HybridmailProviderFlavor + capabilities + document_model + validation_model + option_model + send_model + status_model + tracking_model + return_model + delivery_confirmation_model + evidence_mapping + idempotency_model + testing_model +``` + +This should become the internal structure of each provider flavor implementation. + +## 21. Summary + +Binect, Pingen, and DPAG all fit the `hybridmail-connect` model, but each contributes a different design center: + +```text +Binect = document workflow depth +Pingen = developer/API friendliness +DPAG = postal-product and infrastructure depth +``` + +For the framework, this is good news. The three providers collectively validate the need for a generic hybrid-mail adapter model with provider-specific flavors. + +The most important implementation principle is: + +> Preserve provider-specific status detail, map conservatively to normalized evidence, and never turn physical dispatch terminology into coordination success without explicit product and policy support. + +The most important improvement for coding-agent friendliness is: + +> Publish machine-readable capability descriptors, lifecycle state machines, status/evidence mapping tables, webhook schemas, validation catalogs, idempotency semantics, and golden test fixtures. + diff --git a/spec/HybridmailAdapterSpecification.md b/spec/HybridmailAdapterSpecification.md new file mode 100644 index 0000000..ba4643e --- /dev/null +++ b/spec/HybridmailAdapterSpecification.md @@ -0,0 +1,2076 @@ +# HybridmailAdapterSpecification.md + +## 1. Document Status + +**Document:** HybridmailAdapterSpecification.md +**Project:** hybridmail-connect +**Target Integration:** coordination-engine +**Adapter Contract:** AdapterInterfaceSpecification.md v1.0 +**Specification Version:** 1.1 +**Status:** Updated Draft +**Primary Scope:** Provider-neutral hybrid-mail adapter baseline, provider flavor contract, lifecycle semantics, evidence model, and conformance requirements. + +## 2. Version 1.1 Change Summary + +This version strengthens the original hybrid-mail adapter specification based on provider flavor analysis for: + +* Binect +* Pingen +* Deutsche Post / E-POSTBUSINESS API + +The main changes are: + +1. Added mandatory provider flavor contract. +2. Added machine-readable capability profile. +3. Added layered lifecycle model. +4. Added provider status mapping registry. +5. Added postal product evidence semantics. +6. Strengthened distinction between ordinary dispatch and delivery confirmation. +7. Strengthened idempotency and duplicate physical dispatch prevention. +8. Expanded validation issue taxonomy. +9. Added batch and child-letter semantics. +10. Added polling, webhook, and late-event ingestion model. +11. Added evidence ceiling model. +12. Added coding-agent implementation and conformance requirements. + +## 3. Purpose + +This document specifies the provider-neutral hybrid-mail adapter baseline for `hybridmail-connect`. + +Hybrid mail means that a digital payload, usually a PDF or print-ready document, is submitted to a provider that organizes physical production and postal dispatch. The provider may validate the document, print it, fold it, insert it into an envelope, frank it, hand it over to a postal carrier, and optionally provide tracking, return-mail information, registered-mail evidence, delivery-confirmation evidence, or address-correction information. + +This specification defines the common model that provider-specific flavors MUST map into. + +Known provider flavors include: + +* `HybridmailBinectSpecification.md` +* `HybridmailPingenSpecification.md` +* `HybridmailDpagSpecification.md` + +Future provider flavors SHOULD use the same baseline. + +## 4. Core Principle + +Hybrid mail is a controlled physical-delivery channel initiated through digital submission. + +The adapter MUST distinguish: + +```text +digital submission +provider acceptance +document validation +production readiness +print production +enveloping +franking +handover to postal carrier +postal delivery evidence +registered or tracked delivery evidence +undeliverable / return-mail evidence +physical delivery uncertainty +coordination result evidence +``` + +Normal physical letter dispatch usually cannot prove that the intended recipient personally received, opened, read, understood, or acted on the payload. + +The adapter reports hybrid-mail evidence. `coordination-engine` decides whether that evidence satisfies the coordination case. + +## 5. Relationship to coordination-engine + +`hybridmail-connect` does not own: + +* `CoordinationCase` +* intended result evaluation +* participant-level success +* case-level success +* legal notice interpretation +* proof that the recipient opened or read the letter +* contract acceptance +* payment settlement +* signature completion +* follow-up and escalation policy + +`hybridmail-connect` owns: + +* hybrid-mail provider abstraction +* provider flavor capability declaration +* document submission +* provider identifiers +* document validation result mapping +* print/shipping option mapping +* production status mapping +* postal handover status mapping +* status polling and webhook ingestion +* return-mail / undeliverable status normalization +* registered-letter or delivery-confirmation evidence normalization +* hybrid-mail-native message timeline +* hybrid-mail evidence assessment + +The boundary rule is: + +> hybridmail-connect reports what happened in the hybrid-mail channel and what that may indicate. coordination-engine decides what that means for the coordination case. + +## 6. Hybrid Mail as Delivery Channel + +Within `coordination-engine`, hybrid mail is primarily a **delivery channel**. + +It is used when the communication payload itself is physically delivered as a paper letter. + +Examples: + +```text +PDF letter submitted for physical mailing +invoice printed and posted +contract notice sent by post +dunning letter sent as registered mail +mass mailing sent as bulk/serial letters +confidential employee payroll letter mailed physically +``` + +Hybrid mail may also produce notification-like evidence, because physical payload dispatch can be a strong delivery attempt. However, even physical delivery normally does not prove that the intended human personally read or understood the letter. + +For high-assurance scenarios, hybrid-mail evidence may be combined with: + +* registered-mail evidence +* delivery confirmation +* return-mail processing +* manual evidence +* recipient response +* payment event +* signature event +* portal access +* acknowledgement + +## 7. Normative Provider Flavor Contract + +Every provider-specific implementation MUST define a provider flavor specification. + +A provider flavor specification MUST declare: + +```text +provider identity +capability profile +supported file types +supported postal products +native lifecycle model +native status model +validation issue model +option model +idempotency model +webhook model +polling model +delivery confirmation model +return-mail model +batch/child-letter model +evidence mapping +known limitations +test requirements +``` + +A provider flavor MUST NOT claim support for any lifecycle event, postal product, webhook, delivery confirmation, return-mail process, idempotency behavior, or production-status mapping unless it explicitly declares the capability. + +## 8. Provider Flavor Descriptor + +Each provider flavor MUST expose a machine-readable descriptor. + +```yaml +HybridmailProviderFlavorDescriptor: + provider_name: string + provider_label: string + provider_family: hybridmail + provider_version: string? + adapter_contract_version: string + parent_specification: HybridmailAdapterSpecification.md + deployment_mode: embedded | sidecar | external | unknown + capability_profile: HybridmailCapabilityProfile + supported_file_types: + - string + supported_attachment_file_types: + - string + supported_postal_products: + - HybridmailPostalProduct + status_mapping_ref: string + validation_issue_catalog_ref: string + product_evidence_matrix_ref: string + known_limitations: + - string +``` + +## 9. HybridmailCapabilityProfile + +Provider capabilities MUST be represented explicitly. + +```yaml +HybridmailCapabilityProfile: + supports_document_upload: boolean + supports_validation: boolean + supports_attachments: boolean + supports_preview: boolean + supports_single_letter: boolean + supports_bulk: boolean + supports_serial_letters: boolean + supports_status_polling: boolean + supports_webhooks: boolean + supports_track_and_trace: boolean + supports_production_status: boolean + supports_postal_handover_status: boolean + supports_delivery_confirmation: none | product_dependent | full | unknown + supports_registered_mail: boolean + supports_return_mail: boolean + supports_address_correction: boolean + supports_international_mail: boolean + supports_scheduled_dispatch: boolean + supports_cover_sheet: boolean + supports_address_positioning: boolean + supports_automatic_positioning: boolean + supports_idempotency: native | adapter_managed | none | unknown + supports_staging: boolean + supports_rate_limits: boolean + supports_send_limits: boolean + supports_cost_calculation: boolean + supports_sustainability_options: boolean + supports_cancellation: none | before_submission | before_production | provider_specific | unknown +``` + +A provider flavor MAY add provider-specific capabilities, but these MUST be namespaced or clearly marked as provider-specific. + +## 10. Provider Flavor Examples + +### 10.1 Binect Flavor Center + +Binect is modeled as a document-centric hybrid-mail provider. + +Important capabilities include: + +```text +document upload +attachments +preview +print and shipping options +document status +shipping status +address-position validation +address-format validation +restricted-area validation +serial/child document status +``` + +### 10.2 Pingen Flavor Center + +Pingen is modeled as a developer-friendly letter API with strong operational status features. + +Important capabilities include: + +```text +letter creation +idempotency-key support +webhooks +Track & Trace +rate limits +send limits +staging/simulation +return-mail processing +undeliverable and delivered webhook categories +auto-send +address-position configuration +``` + +### 10.3 DPAG / E-POSTBUSINESS Flavor Center + +DPAG is modeled as a postal-infrastructure-native shipment API. + +Important capabilities include: + +```text +PDF/PDF-A shipment submission +single and bulk upload +bulk status requests +production data +shipment tracking +registered-mail products +address-window positioning +automatic positioning +cover sheets +scheduled dispatch +``` + +These provider centers are informative only. The authoritative behavior must be captured in each provider flavor descriptor and status mapping. + +## 11. Layered Hybrid-Mail Lifecycle + +Hybrid mail MUST be modeled as a layered lifecycle, not as a single flat status. + +## 11.1 Digital Submission Layer + +```text +created +upload_requested +uploaded +accepted_by_provider +rejected_by_provider +upload_failed +``` + +## 11.2 Validation Layer + +```text +validation_pending +validation_passed +validation_failed +action_required +``` + +## 11.3 Preparation Layer + +```text +options_set +preview_available +address_positioning_applied +cover_sheet_generated +ready_for_sending +scheduled +``` + +## 11.4 Production Layer + +```text +submitted_for_sending +production_pending +production_started +production_completed +production_failed +cancelled_before_production +``` + +## 11.5 Postal Layer + +```text +handed_to_postal_service +in_transit +delivery_confirmed +undeliverable +return_received +forwarded +address_corrected +status_unknown +expired_without_final_status +``` + +## 11.6 Critical Lifecycle Rule + +The following transitions MUST NOT be collapsed: + +```text +uploaded ≠ validation_passed +validation_passed ≠ submitted_for_sending +submitted_for_sending ≠ production_completed +production_completed ≠ handed_to_postal_service +handed_to_postal_service ≠ delivery_confirmed +delivery_confirmed ≠ human_read_confirmed +``` + +## 12. Hybrid Mail Lifecycle Model + +The adapter SHOULD support the following lifecycle events where provider capabilities allow them. + +```text +letter.created +letter.payload_received +letter.rendered +letter.render_failed +letter.upload_requested +letter.accepted_by_adapter +letter.rejected_by_adapter +letter.uploaded_to_provider +letter.accepted_by_provider +letter.rejected_by_provider +letter.validation_started +letter.validation_passed +letter.validation_failed +letter.action_required +letter.preview_available +letter.options_set +letter.ready_for_sending +letter.scheduled +letter.submitted_for_sending +letter.cancelled_before_production +letter.processing_started +letter.transferred_for_printing +letter.printed +letter.enveloped +letter.franked +letter.production_completed +letter.handed_to_postal_service +letter.in_postal_delivery +letter.delivery_confirmed +letter.registered_delivery_event +letter.undeliverable_reported +letter.return_mail_received +letter.address_corrected +letter.forwarded +letter.finalized +letter.status_unknown +letter.expired_without_final_status +``` + +Provider flavors MAY expose fewer lifecycle events, but they MUST map their native states conservatively. + +## 13. Weakest Safe Mapping Rule + +If a provider-native status is ambiguous, the adapter MUST map it to the weakest semantically safe normalized event and preserve the raw provider status in metadata. + +Example: + +```text +Native provider status: "sent" +``` + +Possible meanings: + +```text +submitted_for_sending +production_completed +handed_to_postal_service +``` + +Unless provider documentation confirms that `sent` means postal handover, the adapter MUST NOT map it to `delivery.postal.handed_over`. + +Where ambiguity remains, the adapter SHOULD map to: + +```text +delivery.payload.submitted +``` + +or another weaker event and add a warning. + +## 14. Provider Status Mapping Registry + +Every provider flavor MUST define a status mapping registry. + +```yaml +HybridmailStatusMapping: + provider_status: string + provider_status_scope: upload | validation | preparation | production | postal | tracking | return | bulk | child | unknown + normalized_event: string + normalized_state: string + evidence_grade: EvidenceGrade + confidence: low | medium | high + terminal: boolean + retryable: boolean? + product_dependent: boolean + required_product_types: + - string + warning: string? + raw_status_preserved: true +``` + +Example: + +```yaml +- provider_status: SENT + provider_status_scope: postal + normalized_event: delivery.postal.handed_over + normalized_state: handed_to_postal_service + confidence: medium + terminal: false + product_dependent: true + warning: Verify that SENT means postal handover for this provider/product. +``` + +Example conservative mapping: + +```yaml +- provider_status: SENT + provider_status_scope: unknown + normalized_event: delivery.payload.submitted + normalized_state: submitted_for_sending + confidence: low + terminal: false + product_dependent: true + warning: Native SENT status is ambiguous; mapped conservatively. +``` + +## 15. Postal Product Evidence Semantics + +Every provider flavor MUST define postal product semantics. + +```yaml +HybridmailPostalProduct: + product_code: string + provider_product_code: string? + normalized_product_type: standard | priority | registered | registered_return_receipt | delivery_confirmation | international | dialogpost | provider_specific | unknown + supports_tracking: boolean + supports_delivery_confirmation: boolean + supports_recipient_signature: boolean + supports_return_info: boolean + supports_address_correction: boolean + supports_international_delivery: boolean + evidence_ceiling: dispatch_only | in_transit | delivery_confirmed | return_only | product_dependent | unknown + limitations: + - string +``` + +Product semantics constrain evidence mapping. + +If a product does not support delivery confirmation, the adapter MUST NOT emit: + +```text +delivery.postal.delivery_confirmed +``` + +for that product unless another explicit source provides delivery-confirmation evidence. + +## 16. Evidence Ceiling Model + +Each provider/product/status combination has a maximum evidence strength. + +```yaml +HybridmailEvidenceCeiling: + provider_name: string + product_type: string + max_positive_event: delivery.payload.submitted | delivery.production.completed | delivery.postal.handed_over | delivery.postal.in_transit | delivery.postal.delivery_confirmed | unknown + max_positive_strength: weak | medium | strong | conclusive | unknown + can_prove_human_awareness: boolean + can_prove_payload_reading: boolean + can_prove_recipient_identity: boolean + notes: + - string +``` + +Typical examples: + +```yaml +ordinary_letter: + max_positive_event: delivery.postal.handed_over + max_positive_strength: strong + can_prove_human_awareness: false + can_prove_payload_reading: false + can_prove_recipient_identity: false +``` + +```yaml +registered_delivery_confirmation: + max_positive_event: delivery.postal.delivery_confirmed + max_positive_strength: strong + can_prove_human_awareness: false + can_prove_payload_reading: false + can_prove_recipient_identity: product_dependent +``` + +This prevents the coordination policy from demanding evidence a product cannot produce. + +## 17. Ordinary Postal Dispatch vs Delivery Confirmation + +The adapter MUST explicitly distinguish ordinary dispatch from delivery confirmation. + +Normative rules: + +```text +Postal handover is strong dispatch evidence. +Postal handover is not final delivery confirmation. +Absence of return mail is not proof of delivery. +Ordinary letters often do not produce final delivery status. +Delivery confirmation requires product/status evidence that explicitly supports delivery confirmation. +Registered-mail events are product-specific and must not be generalized. +Return mail or undeliverable events may arrive late and must still be recorded. +``` + +Recommended normalized events: + +```text +delivery.postal.handed_over +delivery.postal.in_transit +delivery.postal.delivery_confirmed +delivery.postal.undeliverable +delivery.postal.return_received +delivery.postal.status_unknown +delivery.postal.expired_without_final_status +``` + +Avoid using the ambiguous normalized event: + +```text +delivery.postal.delivered +``` + +If a provider-native status is called `delivered`, it MUST be mapped to `delivery.postal.delivery_confirmed` only if the product/status semantics support delivery confirmation. + +## 18. Letter, Document, Attempt, Batch, and Recipient Model + +The adapter MUST distinguish at least six layers. + +## 18.1 HybridmailLetter + +The logical letter delivery created by a client or coordination case. + +```yaml +HybridmailLetter: + hybridmail_letter_id: string + coordination_case_id: string? + participant_id: string? + purpose: string? + payload_ref: ResourceRef + document_ref: HybridmailDocumentRef? + recipient: PostalRecipient + sender: PostalSender? + options: HybridmailOptions? + tracking_context: TrackingContext + created_at: timestamp +``` + +## 18.2 HybridmailDocumentRef + +The submitted document or generated print artifact. + +```yaml +HybridmailDocumentRef: + document_id: string + provider_document_id: string? + document_type: pdf | pdfa | postscript | image | other + version: string? + page_count: integer? + sheet_count: integer? + color_pages: integer? + grayscale_pages: integer? + simplex_pages: integer? + duplex_pages: integer? + integrity_hash: string? + validation_state: unknown | pending | passed | action_required | failed + metadata: object? +``` + +## 18.3 HybridmailAttempt + +One submission attempt through one provider/configuration. + +```yaml +HybridmailAttempt: + hybridmail_attempt_id: string + hybridmail_letter_id: string + provider_name: string + provider_account_ref: string? + provider_document_id: string? + provider_letter_id: string? + provider_sending_id: string? + provider_tracking_id: string? + state: HybridmailAttemptState + idempotency_key: string + created_at: timestamp + updated_at: timestamp +``` + +## 18.4 HybridmailBatch + +A batch or serial-letter group. + +```yaml +HybridmailBatch: + batch_id: string + provider_batch_id: string? + coordination_case_id: string? + batch_type: bulk | serial | campaign | mixed | unknown + letters: + - hybridmail_letter_id + state: pending | partial | completed | failed | unknown + created_at: timestamp + updated_at: timestamp? +``` + +## 18.5 HybridmailChildLetterStatus + +```yaml +HybridmailChildLetterStatus: + batch_id: string + child_letter_id: string + provider_child_id: string? + hybridmail_letter_id: string? + participant_id: string? + provider_status: string? + normalized_status: string + evidence_event_refs: + - string + updated_at: timestamp? + metadata: object? +``` + +## 18.6 PostalRecipient + +```yaml +PostalRecipient: + recipient_id: string? + name_lines: + - string + organization: string? + street: string? + house_number: string? + address_addition: string? + postal_code: string? + city: string? + region: string? + country_code: string? + country_name: string? + address_source: extracted_from_document | provided_metadata | provider_detected | cover_sheet | manual | unknown + address_quality: PostalAddressQuality? +``` + +## 18.7 PostalSender + +```yaml +PostalSender: + sender_id: string? + name_lines: + - string + organization: string? + return_address: PostalRecipient? + billing_account_ref: string? + sender_identity_ref: string? +``` + +## 19. Batch and Child-Letter Rules + +Batch and serial-letter processing MUST be handled conservatively. + +Normative rules: + +```text +Batch acceptance MUST NOT imply child-letter success. +Batch submission MUST NOT imply child-letter postal handover. +Batch completion MUST NOT imply every participant completed unless child statuses prove it or policy explicitly accepts batch evidence. +Provider flavors MUST emit per-child evidence whenever provider data allows it. +Case-level aggregation belongs to coordination-engine, not hybridmail-connect. +``` + +Batch events should be treated as operational evidence unless linked to child-letter status. + +## 20. Hybrid Mail Options Model + +The adapter MUST support a provider-neutral print and postal option model. + +```yaml +HybridmailOptions: + color_mode: color | grayscale | provider_default | unknown + print_sides: simplex | duplex | provider_default | unknown + envelope_type: c5 | c4 | window_left | window_right | no_window | provider_default | unknown + address_position: left | right | metadata | extracted | automatic | cover_sheet | provider_default | unknown + postage_product: standard | priority | economy | registered | registered_return_receipt | delivery_confirmation | international | dialogpost | provider_specific + country_scope: domestic | international | mixed | unknown + dispatch_mode: manual_review | auto_send | submit_later | scheduled + desired_dispatch_date: date? + cutoff_policy_ref: string? + return_mail_handling: none | provider_processed | scan_return | digital_return_info | physical_return | unknown + address_correction_requested: boolean? + cover_sheet: boolean? + automatic_positioning: boolean? + sustainability_option: none | carbon_reduced | gogreen_plus | provider_default | provider_specific + metadata: object? +``` + +Provider-native option names MUST be preserved in metadata. + +## 21. Postal Address Quality Model + +```yaml +PostalAddressQuality: + validation_state: unknown | valid | action_required | invalid | corrected + country_supported: boolean? + address_position_valid: boolean? + address_format_valid: boolean? + postal_code_valid: boolean? + street_known: boolean? + recipient_known: boolean? + correction_available: boolean? + correction_source: provider | postal_carrier | manual | unknown + issues: + - string +``` + +Address quality may update a contact registry, but it MUST NOT create coordination result success by itself. + +## 22. Document Validation Model + +Document validation is central to hybrid mail. + +```yaml +HybridmailValidationResult: + document_id: string + validation_state: pending | passed | action_required | failed | unknown + issues: + - HybridmailValidationIssue + validated_at: timestamp? + provider_validation_ref: string? +``` + +```yaml +HybridmailValidationIssue: + issue_code: string + provider_issue_code: string? + severity: info | warning | action_required | fatal + category: format | pdf | pdfa | page_size | address | address_position | address_format | restricted_area | margins | cover_sheet | file_size | page_count | attachment | postage | country | scheduled_dispatch | credit_or_account | provider_policy | unknown + message: string + page_number: integer? + bounding_box: object? + fixable: boolean? + machine_recommendation: string? +``` + +## 23. Validation Issue Taxonomy + +Provider flavors SHOULD map native validation issues into this taxonomy. + +```text +invalid_pdf +invalid_pdfa +unsupported_file_type +not_a4_portrait +invalid_page_size +file_too_large +too_many_pages +address_not_detected +address_invalid +address_format_invalid +address_position_invalid +automatic_positioning_failed +restricted_area_violation +barcode_area_violation +margin_violation +cover_sheet_failed +unsupported_country +unsupported_product +attachment_invalid +scheduled_dispatch_invalid +insufficient_credit +account_blocked +provider_policy_rejected +unknown_validation_issue +``` + +Validation failure usually maps to strong failure of the hybrid-mail attempt, but not necessarily failure of the coordination case if another channel or corrected document can be used. + +## 24. Preview and Review Semantics + +Some providers support preview retrieval before dispatch. + +```yaml +HybridmailPreview: + preview_id: string + document_id: string + provider_preview_id: string? + preview_type: pdf | png | image | metadata | unknown + preview_uri: string? + generated_at: timestamp + expires_at: timestamp? + metadata: object? +``` + +Normative rules: + +```text +Preview availability is operational readiness evidence. +Preview availability is not postal dispatch evidence. +Preview approval may be required by scenario policy before sending. +Preview artifacts may contain full sensitive document content and require retention/security rules. +``` + +Preview evidence usually maps to: + +```text +delivery.payload.available +``` + +with metadata. + +## 25. Status Ingestion Model: Polling, Webhooks, and Late Events + +Provider flavors MUST define how status is ingested. + +```yaml +HybridmailStatusIngestionModel: + supports_polling: boolean + supports_webhooks: boolean + polling_required: boolean + webhook_required: boolean + fallback_polling_enabled: boolean + polling_interval_seconds: integer? + max_polling_duration_seconds: integer? + late_event_policy: accept_and_record + terminal_statuses: + - string +``` + +Normative rules: + +```text +Late return-mail events MUST be accepted and recorded. +Late delivery-confirmation events MUST be accepted and recorded. +Late undeliverable events MUST be accepted and recorded. +Polling and webhook events may arrive out of order. +Raw provider status MUST be preserved. +Derived state may be revised or annotated after late events. +``` + +## 26. Hybrid-Mail Event Vocabulary + +This spec defines the following recommended normalized event vocabulary. + +## 26.1 Payload and Validation Events + +```text +delivery.payload.submitted +delivery.payload.accepted +delivery.payload.rejected +delivery.payload.available +delivery.payload.failed +payload.validation_started +payload.validation_passed +payload.validation_failed +``` + +## 26.2 Production Events + +```text +delivery.production.pending +delivery.production.started +delivery.production.completed +delivery.production.failed +``` + +## 26.3 Postal Events + +```text +delivery.postal.handed_over +delivery.postal.in_transit +delivery.postal.delivery_confirmed +delivery.postal.undeliverable +delivery.postal.return_received +delivery.postal.forwarded +delivery.postal.address_corrected +delivery.postal.status_unknown +delivery.postal.expired_without_final_status +``` + +## 26.4 System Events + +```text +system.provider.degraded +system.provider.unavailable +system.adapter.health_changed +``` + +The event `delivery.postal.delivered` SHOULD be avoided as a normalized event because it is ambiguous. Use `delivery.postal.delivery_confirmed` instead. + +## 27. Provider-to-Generic Event Mapping + +Provider flavors MUST provide mapping tables from native events to generic events. + +Example: + +| Provider-native event | Generic event | Evidence interpretation | +| --------------------- | ------------------------------------ | -------------------------------------------- | +| Upload accepted | `delivery.payload.accepted` | Digital submission accepted | +| Validation passed | `payload.validation_passed` | Provider-processable | +| Validation failed | `payload.validation_failed` | Correction/fallback needed | +| Submitted for sending | `delivery.payload.submitted` | Provider instructed to send | +| Production started | `delivery.production.started` | Physical production started | +| Production completed | `delivery.production.completed` | Production completed, not postal delivery | +| Handover to post | `delivery.postal.handed_over` | Strong dispatch evidence | +| In transit | `delivery.postal.in_transit` | Tracking evidence | +| Delivery confirmed | `delivery.postal.delivery_confirmed` | Product-dependent physical-delivery evidence | +| Undeliverable | `delivery.postal.undeliverable` | Strong negative delivery evidence | +| Return received | `delivery.postal.return_received` | Strong negative delivery evidence | + +## 28. Hybrid Mail Evidence Assessment + +`hybridmail-connect` should provide a hybrid-mail-native assessment separate from coordination-engine state. + +```yaml +HybridmailEvidenceAssessment: + hybridmail_letter_id: string + participant_id: string? + category: success | fail | undef + subclass: string + confidence: low | medium | high + strongest_signal: string? + evidence_summary: + - string + recommended_coordination_interpretation: string? +``` + +The assessment categories are adapter-level hints. + +## 29. Hybrid-Mail Adapter Success Subclasses + +Hybrid-mail-level `success` means the channel produced meaningful hybrid-mail evidence. + +Possible subclasses: + +```text +success.provider_accepted +success.validation_passed +success.preview_available +success.submitted_for_sending +success.production_started +success.production_completed +success.handed_to_postal_service +success.in_transit +success.registered_tracking_event +success.delivery_confirmed +success.return_info_processed +``` + +Important: These are not automatically coordination success. + +For ordinary letters, `success.handed_to_postal_service` is strong dispatch evidence but not proof of physical delivery or human awareness. + +## 30. Hybrid-Mail Adapter Fail Subclasses + +Hybrid-mail-level `fail` indicates strong evidence that the hybrid-mail attempt failed. + +Subclasses: + +```text +fail.missing_payload +fail.invalid_document +fail.validation_failed +fail.action_required_unresolved +fail.invalid_address +fail.address_position_invalid +fail.address_format_invalid +fail.restricted_area_violation +fail.unsupported_country +fail.unsupported_product +fail.insufficient_credit +fail.account_blocked +fail.provider_rejected +fail.upload_failed +fail.production_failed +fail.cancelled +fail.postal_undeliverable +fail.return_mail_received +fail.expired_before_handover +``` + +## 31. Hybrid-Mail Adapter Undef Subclasses + +Hybrid-mail-level `undef` is used when the adapter cannot determine physical delivery, recipient awareness, or final postal outcome. + +Subclasses: + +```text +undef.pending +undef.uploaded_only +undef.provider_accepted_only +undef.validation_pending +undef.action_required +undef.ready_but_not_sent +undef.scheduled_for_future_dispatch +undef.submitted_for_sending +undef.processing +undef.production_status_unknown +undef.production_completed_but_handover_unproven +undef.transferred_for_printing +undef.handed_to_postal_service_but_delivery_unproven +undef.postal_delivery_unknown +undef.registered_tracking_pending +undef.return_mail_pending +undef.no_final_status_expected +undef.bulk_status_partial +undef.conflicting_evidence +undef.channel_degraded +``` + +For ordinary physical letters, the adapter should expect `undef.handed_to_postal_service_but_delivery_unproven` to be common. + +## 32. Detailed Scenario Classification + +## 32.1 Pre-Submission Scenarios + +| Scenario | Hybridmail assessment | Normalized event | Notes | +| -------------------------------------- | --------------------------------------------- | --------------------------- | ----------------------------- | +| Missing payload | `fail.missing_payload` | `delivery.payload.failed` | No delivery possible | +| Unsupported document format | `fail.invalid_document` | `payload.validation_failed` | Provider may reject | +| Invalid PDF/PDF-A | `fail.validation_failed` | `payload.validation_failed` | Provider-specific | +| Unsupported page size | `fail.validation_failed` | `payload.validation_failed` | Common hybrid-mail constraint | +| Address not detected | `fail.invalid_address` or `action_required` | `payload.validation_failed` | May be fixable | +| Address in wrong window position | `fail.validation_failed` or `action_required` | `payload.validation_failed` | Provider-specific | +| Restricted area violation | `fail.validation_failed` or `action_required` | `payload.validation_failed` | Print/postal layout issue | +| Unsupported destination country | `fail.unsupported_country` | `delivery.payload.rejected` | Route unavailable | +| Unsupported postal product | `fail.unsupported_product` | `delivery.payload.rejected` | Option mismatch | +| Insufficient credit/account problem | `fail.insufficient_credit` | `delivery.payload.rejected` | Operational/account failure | +| Provider unavailable before acceptance | `undef.pending` or provider fail | action error | Retry-dependent | + +## 32.2 Upload and Validation Scenarios + +| Scenario | Hybridmail assessment | Normalized event | Notes | +| ------------------------ | ------------------------------------------ | ---------------------------------------------------- | --------------------------- | +| Adapter accepted request | `undef.pending` | `delivery.payload.submitted` | Adapter accepted work | +| Provider accepted upload | `undef.provider_accepted_only` | `delivery.payload.accepted` | Digital submission accepted | +| Provider rejected upload | `fail.provider_rejected` | `delivery.payload.rejected` | Strong attempt failure | +| Validation started | `undef.validation_pending` | `payload.validation_started` | Operational status | +| Validation passed | `success.validation_passed` | `payload.validation_passed` | Ready for options/send | +| Action required | `fail.action_required_unresolved` or undef | `payload.validation_failed` with severity | May be fixable | +| Validation failed fatal | `fail.validation_failed` | `payload.validation_failed` | Cannot send | +| Preview available | operational success | `delivery.payload.available` | Review evidence only | +| Attachment accepted | operational success | `delivery.payload.accepted` with attachment metadata | Not final delivery | +| Attachment failed | `fail.validation_failed` | `payload.validation_failed` | Must be corrected | + +## 32.3 Sending and Production Scenarios + +| Scenario | Hybridmail assessment | Normalized event | Notes | +| ----------------------------- | ------------------------------------- | ------------------------------------------------ | --------------------------- | +| Options set | operational success | metadata event | Not delivery | +| Ready for sending | `undef.ready_but_not_sent` | `delivery.payload.available` | Awaiting dispatch approval | +| Scheduled for future dispatch | `undef.scheduled_for_future_dispatch` | `delivery.payload.available` | Dispatch pending | +| Submitted for sending | `success.submitted_for_sending` | `delivery.payload.submitted` | Provider instructed to send | +| Cancelled before production | `fail.cancelled` or cancelled | `delivery.payload.failed` or manual cancellation | Depends on policy | +| Processing started | `undef.processing` | `delivery.production.started` | Production chain began | +| Transferred for printing | `success.production_started` | `delivery.production.started` | Strong production evidence | +| Printed | production success | `delivery.production.completed` | Not postal handover | +| Enveloped/franked | production success | `delivery.production.completed` | Not postal delivery | +| Production failed | `fail.production_failed` | `delivery.production.failed` | Strong channel failure | +| Handover to postal service | `success.handed_to_postal_service` | `delivery.postal.handed_over` | Strong dispatch evidence | +| Cutoff missed | `undef.processing` or delayed | metadata/channel warning | Adjust expected timing | + +## 32.4 Postal Delivery Scenarios + +| Scenario | Hybridmail assessment | Normalized event | Notes | +| ------------------------------ | ------------------------------------------------------ | ----------------------------------------- | ---------------------------------- | +| Ordinary letter handed over | `undef.handed_to_postal_service_but_delivery_unproven` | `delivery.postal.handed_over` | Strong dispatch, delivery unproven | +| Postal tracking ID received | `undef.postal_delivery_unknown` | `delivery.postal.in_transit` | Stronger traceability | +| Registered tracking event | `success.registered_tracking_event` | `delivery.postal.in_transit` or confirmed | Depends event | +| Delivery confirmed | `success.delivery_confirmed` | `delivery.postal.delivery_confirmed` | Strong physical delivery evidence | +| Return receipt received | `success.delivery_confirmed` | `delivery.postal.delivery_confirmed` | Stronger evidence | +| Undeliverable reported | `fail.postal_undeliverable` | `delivery.postal.undeliverable` | Strong negative evidence | +| Return mail received | `fail.return_mail_received` | `delivery.postal.return_received` | Strong negative evidence | +| Address corrected | address signal | `delivery.postal.address_corrected` | Useful for contact registry | +| Forwarded due to moving | `undef.forwarded` or success-ish | `delivery.postal.forwarded` | Delivery path changed | +| No final status expected | `undef.no_final_status_expected` | metadata | Common for ordinary letters | +| No return after waiting period | still `undef` unless policy accepts | timeout metadata | Absence of return is not proof | + +## 33. Evidence Grading Rules + +## 33.1 Provider Upload Accepted + +```yaml +event_type: delivery.payload.accepted +evidence_grade: + strength: weak + actor_certainty: none + authority_certainty: none + payload_certainty: medium + interaction_certainty: none + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: low +notes: + - Provider accepted the digital submission. + - Does not prove validation, production, postal handover, or delivery. +``` + +## 33.2 Validation Passed + +```yaml +event_type: payload.validation_passed +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Document passed provider validation for print/postal processing. + - Does not prove dispatch or delivery. +``` + +## 33.3 Validation Failed + +```yaml +event_type: payload.validation_failed +evidence_grade: + strength: negative + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Document cannot be processed until corrected or replaced. +``` + +## 33.4 Submitted for Sending + +```yaml +event_type: delivery.payload.submitted +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Letter was submitted into the provider sending process. + - Does not prove print production or postal handover. +``` + +## 33.5 Production Started + +```yaml +event_type: delivery.production.started +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: medium + channel_certainty: high + non_repudiation_strength: low +notes: + - Letter entered production or printing process. + - Does not prove postal handover. +``` + +## 33.6 Production Completed + +```yaml +event_type: delivery.production.completed +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Letter production was completed. + - Does not prove postal handover unless provider status explicitly says so. +``` + +## 33.7 Handed to Postal Service + +```yaml +event_type: delivery.postal.handed_over +evidence_grade: + strength: strong + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - Letter was handed over to the postal delivery chain. + - Strong dispatch evidence. + - Does not prove recipient receipt or reading. +``` + +## 33.8 In Transit + +```yaml +event_type: delivery.postal.in_transit +evidence_grade: + strength: strong + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - Postal tracking indicates the letter is in the postal chain. + - Does not necessarily prove final delivery. +``` + +## 33.9 Delivery Confirmed + +```yaml +event_type: delivery.postal.delivery_confirmed +evidence_grade: + strength: strong + actor_certainty: low + authority_certainty: none + payload_certainty: high + interaction_certainty: low + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - Postal delivery confirmation or registered-mail event was received. + - This is strong physical-delivery evidence. + - It may still not prove that the intended human personally read the payload. +``` + +## 33.10 Return Mail / Undeliverable + +```yaml +event_type: delivery.postal.return_received +evidence_grade: + strength: negative + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - Letter was returned or reported undeliverable. + - Strong evidence that physical delivery failed for this attempt. +``` + +## 34. Status Timeline API + +`hybridmail-connect` SHOULD expose a delivery timeline suitable for standalone diagnostics and coordination audit. + +```yaml +HybridmailTimeline: + hybridmail_letter_id: string + provider_name: string + provider_document_id: string? + provider_letter_id: string? + provider_sending_id: string? + provider_tracking_id: string? + events: + - HybridmailTimelineEvent + current_assessment: HybridmailEvidenceAssessment +``` + +```yaml +HybridmailTimelineEvent: + event_id: string + event_type: string + occurred_at: timestamp + source: adapter | provider | print_center | postal_carrier | return_mail | webhook | polling | operator + native_event_type: string? + normalized_event_type: string? + provider_status: string? + summary: string + evidence_grade: EvidenceGrade + raw_event_ref: string? +``` + +## 35. Adapter Descriptor + +`hybridmail-connect` MUST expose an `AdapterDescriptor` compatible with AdapterInterfaceSpecification.md v1.0. + +```yaml +adapter_id: hybridmail-connect.default +adapter_name: hybridmail-connect +adapter_version: 1.1.0 +adapter_contract_version: 1.0 +adapter_types: + - delivery + - communication + - document + - archive +provider_family: hybridmail +provider_name: configurable +deployment_mode: external +supported_channels: + - hybridmail + - postal_mail +supported_endpoint_types: + - postal_address +supported_actions: + - action_type: delivery.submit_letter + mode: async + idempotency_required: true + - action_type: delivery.submit_bulk + mode: async + idempotency_required: true + - action_type: delivery.validate_payload + mode: async + idempotency_required: true + - action_type: delivery.add_attachment + mode: async + idempotency_required: true + - action_type: delivery.set_options + mode: sync + idempotency_required: true + - action_type: delivery.request_preview + mode: async + idempotency_required: true + - action_type: delivery.approve_for_sending + mode: async + idempotency_required: true + - action_type: delivery.cancel + mode: async + idempotency_required: true + - action_type: delivery.request_status + mode: sync + idempotency_required: true +emitted_event_types: + - delivery.payload.submitted + - delivery.payload.accepted + - delivery.payload.rejected + - payload.validation_started + - payload.validation_passed + - payload.validation_failed + - delivery.payload.available + - delivery.production.pending + - delivery.production.started + - delivery.production.completed + - delivery.production.failed + - delivery.postal.handed_over + - delivery.postal.in_transit + - delivery.postal.delivery_confirmed + - delivery.postal.undeliverable + - delivery.postal.return_received + - delivery.postal.forwarded + - delivery.postal.address_corrected + - delivery.postal.status_unknown + - delivery.postal.expired_without_final_status + - system.provider.degraded + - system.provider.unavailable +evidence_profile: + strongest_evidence_level: strong + can_prove_human_awareness: false + can_prove_payload_delivery: partially + can_prove_identity: false +identity_profile: + identity_strength: none_to_low + authority_strength: none +limitations: + - Ordinary postal handover does not prove recipient receipt. + - Ordinary physical letters often do not provide final delivery confirmation. + - Delivery confirmation depends on postal product and provider support. + - Return-mail information may arrive late. + - Address extraction and validation differ by provider. + - Cancellation may only be possible before production starts. + - Batch-level status does not imply child-letter success. +``` + +## 36. Action Request Handling + +## 36.1 `delivery.submit_letter` + +`delivery.submit_letter` submits a letter payload to a hybrid-mail provider. + +Required fields: + +```text +request_id +action_type +coordination_case_id +participant_id +payload_ref +target_endpoint +tracking_context +idempotency_key +requested_at +``` + +Example: + +```yaml +request_id: req_001 +action_type: delivery.submit_letter +coordination_case_id: case_123 +participant_id: participant_456 +channel: hybridmail +target_endpoint: + endpoint_type: postal_address + value: postal_recipient_ref_456 +payload_ref: + ref_type: coordination_payload + ref_id: payload_777 + uri: s3://bucket/document.pdf + integrity_hash: sha256:... +tracking_context: + correlation_id: corr_789 + coordination_case_id: case_123 + participant_id: participant_456 + delivery_id: delivery_001 + payload_id: payload_777 +idempotency_key: case_123:participant_456:hybridmail:delivery_001 +requested_at: 2026-01-01T12:00:00Z +metadata: + hybridmail_options: + color_mode: grayscale + print_sides: duplex + envelope_type: window_left + postage_product: standard +``` + +## 36.2 Action Result + +The adapter returns: + +```yaml +request_id: req_001 +adapter_id: hybridmail-connect.default +accepted: true +action_state: accepted +adapter_operation_id: hmop_001 +provider_operation_id: providerop_001 +initial_events: + - event_type: delivery.payload.submitted +received_at: 2026-01-01T12:00:01Z +``` + +This result does not prove validation, production, postal handover, or delivery. + +## 37. Idempotency and Duplicate Physical Dispatch Prevention + +Duplicate physical dispatch prevention is safety-critical. + +Normative rules: + +```text +Hybrid-mail submit/send actions MUST be idempotent. +If the provider lacks native idempotency, the adapter MUST implement adapter-managed idempotency. +Duplicate physical dispatch prevention is mandatory. +A repeated request with the same idempotency key MUST NOT create a second physical send. +``` + +Recommended idempotency scope: + +```text +coordination_case_id +participant_id +delivery_id +payload_id +payload_integrity_hash +provider_flavor +postal recipient +postal product +shipment options +scheduled dispatch date +``` + +Idempotency conflict rule: + +```text +Same idempotency key + different payload hash/address/options/product/schedule = conflict. +``` + +The adapter MUST reject idempotency conflicts unless scenario policy explicitly allows a new delivery attempt with a new key. + +## 38. Provider Abstraction + +`hybridmail-connect` SHOULD support a provider abstraction layer. + +Provider integration responsibilities: + +* authenticate +* upload document +* create letter/document record +* add attachments +* configure options +* request preview +* submit for sending +* cancel where possible +* poll status +* ingest webhooks where supported +* normalize provider statuses +* parse validation issues +* parse production data +* parse tracking events +* parse return-mail information +* parse delivery-confirmation information +* expose provider health + +Provider model: + +```yaml +HybridmailProvider: + provider_name: string + provider_account_ref: string + flavor_descriptor_ref: string + supported_features: + - upload + - validation + - attachments + - options + - preview + - send + - cancel + - status_polling + - webhooks + - track_and_trace + - production_status + - return_mail + - registered_mail + - delivery_confirmation + - bulk + - staging + event_mapping_ref: string + configuration_ref: string +``` + +Provider-specific modules should map to the hybrid-mail-native model first, then to normalized coordination events. + +```text +Provider event +→ hybridmail-native event +→ HybridmailEvidenceAssessment +→ EvidenceEvent for coordination-e``` + +## 39. Return Mail and Undeliverable Model + +```yaml +HybridmailReturnEvent: + return_event_id: string + hybridmail_letter_id: string + provider_return_id: string? + postal_tracking_id: string? + return_type: undeliverable | address_unknown | recipient_moved | refused | deceased | insufficient_address | mailbox_inaccessible | other | unknown + address_correction: PostalRecipient? + return_document_ref: ResourceRef? + occurred_at: timestamp? + observed_at: timestamp + raw_event_ref: string? +``` + +Return events usually create strong negative delivery evidence but may also provide address-correction information useful for future attempts. + +## 40. Registered / Delivery Confirmation Model + +```yaml +HybridmailDeliveryConfirmation: + confirmation_id: string + hybridmail_letter_id: string + provider_tracking_id: string? + postal_tracking_id: string? + product_type: registered | registered_return_receipt | delivery_confirmation | provider_specific + status: in_transit | attempted | delivered | refused | undeliverable | returned | unknown + recipient_signature_ref: ResourceRef? + confirmation_document_ref: ResourceRef? + occurred_at: timestamp? + observed_at: timestamp + raw_event_ref: string? +``` + +Delivery confirmation is stronger than ordinary handover evidence, but the exact legal and business meaning remains scenario-specific. + +## 41. Channel Health + +`hybridmail-connect` SHOULD expose hybrid-mail channel health. + +```yaml +HybridmailChannelHealth: + provider_name: string + provider_account_ref: string? + provider_flavor: string? + status: healthy | degraded | failing | unknown + authentication_status: valid | expired | missing | insufficient | unknown + upload_status: healthy | degraded | unavailable | unknown + validation_status: healthy | degraded | unavailable | unknown + preview_status: healthy | degraded | unavailable | not_supported | unknown + production_status: healthy | delayed | unavailable | unknown + postal_handover_status: healthy | delayed | unavailable | unknown + webhook_status: healthy | degraded | unavailable | not_supported | unknown + polling_status: healthy | degraded | unavailable | unknown + cutoff_status: before_cutoff | after_cutoff | no_production_day | unknown + rate_limit_status: available | limited | exhausted | not_supported | unknown + send_limit_status: available | limited | exhausted | not_supported | unknown + known_degradations: + - string +``` + +Health-related normalized events: + +```text +system.provider.degraded +system.provider.unavailable +system.adapter.health_changed +``` + +## 42. Security Requirements + +`hybridmail-connect` MUST: + +* protect provider credentials. +* protect uploaded payloads and document references. +* support secure transport to providers. +* preserve idempotency. +* avoid duplicate physical sends. +* validate provider webhooks where supported. +* avoid logging sensitive document content. +* protect postal recipient data. +* protect previews and generated artifacts. +* support tenant/account separation where applicable. +* support secure deletion or retention rules for raw payloads and previews. +* clearly separate test/staging and production modes. + +Duplicate-send prevention is especially critical because physical mail sends cost money and may have legal/business consequences. + +## 43. Privacy Requirements + +`hybridmail-connect` SHOULD: + +* store payload references instead of payload content where possible. +* support metadata-only mode. +* support masking of postal addresses. +* support raw event redaction. +* support configurable retention of documents, previews, generated cover sheets, and raw events. +* avoid unnecessary storage of print payloads after submission. +* separate operational diagnostics from coordination evidence. +* support deletion or anonymization workflows where legally possible. +* document provider-side retention limitations. + +## 44. Reliability Requirements + +`hybridmail-connect` MUST support: + +* idempotent submit requests. +* duplicate webhook event detection. +* out-of-order event handling. +* late return-mail events. +* late undeliverable events. +* late delivery-confirmation events. +* retryable upload failures. +* non-retryable validation failures. +* provider timeout handling. +* provider polling fallback where webhooks are unavailable. +* correlation preservation. +* dead-letter handling for unprocessable events. +* batch partial-failure handling. +* status revision or annotation after late events. + +Late events MUST be preserved. + +Example: + +```text +Letter handed to postal service on Monday. +Coordination case moves to waiting state. +Return mail event arrives two weeks later. +The return event must still be recorded and emitted as negative evidence. +``` + +## 45. Raw Event Preservation + +`hybridmail-connect` SHOULD preserve raw provider events or references to them. + +```yaml +RawHybridmailEventRef: + raw_event_id: string + provider_name: string + source: api_response | webhook | polling | print_center | postal_carrier | return_mail | tracking | operator + storage_ref: string? + received_at: timestamp + redacted: boolean +``` + +Normalized events should reference raw event data where available: + +```yaml +raw_event_ref: raw_hybridmail_event_123 +``` + +## 46. Minimal API Surface + +`hybridmail-connect` SHOULD expose a headless API. + +## 46.1 Adapter Contract API + +Required conceptual operations: + +```text +GET /adapter/descriptor +GET /adapter/health +POST /adapter/actions +POST /adapter/events/provider +GET /adapter/events +GET /adapter/letters/{id}/timeline +GET /adapter/letters/{id}/assessment +``` + +The actual transport may differ, but these conceptual operations should exist. + +## 46.2 Standalone API + +Useful standalone operations: + +```text +POST /hybridmail/letters +POST /hybridmail/batches +POST /hybridmail/letters/{id}/attachments +PUT /hybridmail/letters/{id}/options +POST /hybridmail/letters/{id}/preview +POST /hybridmail/letters/{id}/send +POST /hybridmail/letters/{id}/cancel +GET /hybridmail/letters/{id} +GET /hybridmail/letters/{id}/timeline +GET /hybridmail/letters/{id}/assessment +GET /hybridmail/letters/{id}/return-info +GET /hybridmail/letters/{id}/delivery-confirmation +GET /hybridmail/channel-health +GET /hybridmail/providers/{provider}/capabilities +GET /hybridmail/providers/{provider}/status-mapping +``` + +## 47. Coding-Agent Implementation Requirements + +Every provider flavor repository or provider-flavor module SHOULD include: + +```text +ProviderFlavorDescriptor.yaml +ProviderCapabilityProfile.yaml +ProviderStatusMapping.yaml +ProviderValidationIssueCatalog.yaml +ProviderProductEvidenceMatrix.yaml +ProviderExampleFlows.md +ProviderGoldenTests.md +``` + +## 47.1 ProviderStatusMapping.yaml + +Each provider flavor SHOULD provide an agent-readable status mapping. + +```yaml +- provider_status: SENT + provider_status_scope: postal + normalized_event: delivery.postal.handed_over + normalized_state: handed_to_postal_service + evidence_strength: strong + confidence: medium + product_dependent: true + warning: Verify that SENT means postal handover for this provider/product. + +- provider_status: DELIVERED + provider_status_scope: postal + normalized_event: delivery.postal.delivery_confirmed + normalized_state: delivery_confirmed + evidence_strength: strong + confidence: product_dependent + product_dependent: true + warning: Only valid for delivery-confirmation-capable products. +``` + +## 47.2 ProviderGoldenTests.md + +Every provider flavor SHOULD define test cases for: + +```text +valid single letter +invalid PDF +address-position failure +restricted-area violation +unsupported country +duplicate idempotency key +idempotency conflict +successful validation +sending submission +production status +postal handover +late return-mail event +delivery-confirmation product event +bulk partial failure +provider timeout +ambiguous provider status +``` + +## 48. Conformance Checklist for Provider Flavors + +A provider flavor conforms to this specification if it: + +1. Provides a `HybridmailProviderFlavorDescriptor`. +2. Provides a `HybridmailCapabilityProfile`. +3. Declares supported file types. +4. Declares supported postal products. +5. Declares product evidence ceilings. +6. Defines a provider status mapping registry. +7. Defines validation issue mapping. +8. Defines idempotency behavior. +9. Prevents duplicate physical dispatch. +10. Preserves raw provider status. +11. Uses weakest safe mapping for ambiguous statuses. +12. Separates upload, validation, sending, production, postal handover, and delivery confirmation. +13. Represents ordinary handover as dispatch evidence, not final delivery proof. +14. Accepts and records late return/undeliverable/delivery-confirmation events. +15. Handles batch and child-letter evidence separately. +16. Provides a timeline API. +17. Provides a provider-specific evidence assessment. +18. Integrates with AdapterInterfaceSpecification.md v1.0. +19. Documents known limitations. +20. Avoids overclaiming coordination result success. + +## 49. Example End-to-End Flows + +## 49.1 Ordinary Physical Letter + +1. `coordination-engine` creates a coordination case. +2. A PDF payload is registered. +3. Policy selects hybrid mail as the delivery channel. +4. `coordination-engine` sends `delivery.submit_letter` to `hybridmail-connect`. +5. `hybridmail-connect` uploads the document to the provider. +6. Provider validates the document. +7. `hybridmail-connect` emits `payload.validation_passed`. +8. The letter is submitted for sending. +9. Provider transfers the letter for printing. +10. Print center hands the letter to the postal service. +11. `hybridmail-connect` emits `delivery.postal.handed_over`. +12. `coordination-engine` treats the participant as physically dispatched, but not necessarily as confirmed delivered unless policy accepts handover as sufficient. + +## 49.2 Validation Failure and Correction + +1. A PDF is uploaded. +2. Provider detects address-position or restricted-area issue. +3. `hybridmail-connect` emits `payload.validation_failed` with issue details. +4. `coordination-engine` marks the participant delivery attempt failed or action-required. +5. Policy requests correction, alternate document generation, or alternate channel. +6. Corrected payload is submitted with a new idempotency key or corrected version reference. + +## 49.3 Registered Letter + +1. A dunning letter is submitted with registered-mail option. +2. Provider validates and submits it. +3. Letter is handed to postal service. +4. Postal tracking events are received. +5. Delivery confirmation or return receipt event arrives. +6. `hybridmail-connect` emits `delivery.postal.delivery_confirmed`. +7. `coordination-engine` evaluates whether this satisfies the intended result. + +## 49.4 Return Mail + +1. Ordinary letter is handed to postal service. +2. No ordinary delivery confirmation is expected. +3. After several days or weeks, return mail is processed. +4. `hybridmail-connect` emits `delivery.postal.return_received`. +5. `coordination-engine` reopens, annotates, or fails the participant delivery state depending on policy. + +## 49.5 Bulk Mailing + +1. `coordination-engine` creates a batch delivery case. +2. `hybridmail-connect` submits a bulk/serial set of letters. +3. Some letters pass validation, others fail. +4. Per-letter evidence events are emitted. +5. `coordination-engine` aggregates participant states and avoids treating batch acceptance as universal success. + +## 50. MVP Scope + +The first useful version of `hybridmail-connect` should implement: + +1. Adapter descriptor. +2. Provider flavor descriptor. +3. Capability profile. +4. Provider status mapping registry. +5. Adapter health endpoint. +6. `delivery.submit_letter`. +7. Idempotent submit request handling. +8. Simulated provider or one real provider flavor. +9. Letter, document, attempt, and recipient records. +10. Basic validation result mapping. +11. Basic print/shipping option mapping. +12. Basic status polling or webhook ingestion. +13. Evidence event generation. +14. Letter timeline. +15. Hybrid-mail evidence assessment. +16. Weakest safe mapping for ambiguous statuses. +17. Duplicate physical send prevention. + +## 51. MVP Required Hybrid-Mail Events + +```text +delivery.payload.submitted +delivery.payload.accepted +delivery.payload.rejected +payload.validation_started +payload.validation_passed +payload.validation_failed +delivery.payload.available +delivery.production.started +delivery.production.completed +delivery.postal.handed_over +delivery.postal.undeliverable +delivery.postal.return_received +delivery.postal.status_unknown +system.provider.degraded +system.provider.unavailable +``` + +## 52. MVP Acceptance Criteria + +The MVP is acceptable when it can: + +1. Accept a coordination-compatible hybrid-mail submit request. +2. Dispatch or simulate a PDF letter submission. +3. Preserve correlation and idempotency. +4. Prevent duplicate physical sends for duplicate idempotency keys. +5. Record provider acceptance separately from validation. +6. Classify validation failure as strong negative payload evidence. +7. Classify postal handover as strong dispatch evidence but not proof of human awareness. +8. Classify ordinary no-final-status cases as unresolved rather than success. +9. Record return mail as strong negative evidence. +10. Provide a letter timeline. +11. Provide a hybrid-mail evidence assessment. +12. Expose a provider capability profile. +13. Expose a provider status mapping registry. +14. Integrate with coordination-engine without overclaiming success. + +## 53. Future Extensions + +Potential future capabilities: + +* multi-provider routing +* provider failover +* cost optimization by product/provider +* print-preview comparison +* PDF/A conversion support +* advanced address extraction +* address correction feedback loop +* return-mail scan ingestion +* registered-mail evidence archive +* delivery-confirmation document archive +* production SLA monitoring +* cutoff-aware scheduling +* country-specific postal product model +* automated correction workflows +* physical plus digital fallback patterns +* duplicate-content detection +* batch campaign analytics +* monthly journal / billing export integration +* print layout linting +* DIN 5008 address-window validation +* provider certification test suites +* coding-agent generated provider flavor tests + +## 54. Non-Goals + +`hybridmail-connect` is not: + +* a document authoring system +* a PDF renderer by default +* a postal carrier +* a legal notice system by itself +* a signature system +* a payment system +* a CRM +* a full workflow engine +* the owner of coordination case success + +It may integrate with such systems or be used by them. + +## 55. Summary + +`hybridmail-connect` models hybrid mail as a physical delivery channel initiated through digital submission. + +Its job is to: + +* submit PDF or print-ready documents +* validate hybrid-mail payloads +* map provider-specific print and shipping options +* track production and postal handover +* process return-mail and undeliverable evidence +* normalize registered or delivery-confirmation evidence +* expose document, letter, batch, provider, and channel diagnostics +* expose provider flavor capabilities and status mappings +* integrate cleanly with `coordination-engine` + +The key rule is: + +> Hybrid-mail events are physical production and postal-chain evidence, not automatic coordination result satisfaction. hybridmail-connect reports hybrid-mail facts and uncertainty. coordination-engine evaluates intended results. + diff --git a/spec/HybridmailBinectSpecification.md b/spec/HybridmailBinectSpecification.md new file mode 100644 index 0000000..6051815 --- /dev/null +++ b/spec/HybridmailBinectSpecification.md @@ -0,0 +1,1057 @@ +# HybridmailBinectSpecification.md + +## 1. Document Status + +**Document:** HybridmailBinectSpecification.md +**Project:** hybridmail-connect +**Provider Flavor:** Binect +**Parent Specification:** HybridmailAdapterSpecification.md +**Target Integration:** coordination-engine +**Adapter Contract:** AdapterInterfaceSpecification.md v1.0 +**Status:** Draft v1.0 +**Primary Scope:** Binect-specific hybrid-mail provider flavor for document upload, validation, attachments, preview, sending, and status mapping. + +## 2. Purpose + +This document specifies the Binect provider flavor for `hybridmail-connect`. + +The Binect flavor implements the generic hybrid-mail adapter model for the Binect API. It maps Binect’s document-centric API workflow into the provider-neutral hybrid-mail model used by `coordination-engine`. + +The purpose of this document is to capture: + +* Binect-specific provider capabilities. +* Binect-specific workflow stages. +* Binect document, attachment, preview, sending, and status concepts. +* Binect validation semantics. +* Binect print and shipping option mapping. +* Binect evidence event mapping. +* Binect-specific limitations and assumptions. +* Binect-specific MVP boundaries. + +The Binect flavor MUST remain compatible with the generic `HybridmailAdapterSpecification.md` and MUST NOT leak Binect-specific terminology into the core coordination model except through explicitly mapped provider metadata. + +## 3. Grounding Summary + +The public Binect API material indicates the following relevant features: + +* Binect provides a REST API for digital business-letter dispatch. +* The API supports document upload. +* Attachments can be added to letters. +* Supported attachment formats include `pdf`, `rtf`, `txt`, `png`, `jpg`, `jpeg`, `bmp`, `gif`, and `ps`. +* Attachment size is limited to 20 MB according to the public FAQ. +* The API supports setting print and shipping options. +* The API supports preview retrieval. +* The API supports sending after preparation. +* The API supports document and shipping status queries. +* Serial or child document status handling is relevant. +* Validation includes address position, address format, and restricted-area checks. +* Binect handles full-service physical dispatch including printing, enveloping/franking, and handover to Deutsche Post. +* Binect publicly positions the API as a way to integrate digital business-letter dispatch into DMS, CRM, and other software systems. + +These facts define the baseline for this provider flavor. + +## 4. Provider Flavor Identity + +```yaml +HybridmailProviderFlavorDescriptor: + provider_name: binect + provider_label: Binect API + provider_family: hybridmail + adapter_contract_version: "1.0" + parent_specification: HybridmailAdapterSpecification.md + deployment_mode: external +``` + +## 5. Binect Capability Descriptor + +The Binect flavor MUST expose a capability descriptor. + +```yaml +HybridmailProviderFlavorDescriptor: + provider_name: binect + provider_version: provider_specific + supported_file_types: + - pdf + - postscript + supported_attachment_file_types: + - pdf + - rtf + - txt + - png + - jpg + - jpeg + - bmp + - gif + - ps + attachment_max_size_mb: 20 + supports_single_letter: true + supports_bulk: true + supports_serial_letters: true + supports_attachments: true + supports_preview: true + supports_webhooks: provider_specific + supports_polling: true + supports_registered_mail: provider_specific + supports_delivery_confirmation: provider_specific + supports_return_mail: provider_specific + supports_address_correction: provider_specific + supports_international_mail: provider_specific + supports_cancellation: provider_specific + supports_staging: provider_specific + supports_idempotency: adapter_managed + supports_rate_limits: provider_specific + supports_cost_calculation: true + supports_document_status: true + supports_shipping_status: true + supports_child_document_status: true + supported_print_options: + color_mode: provider_specific + simplex_duplex: provider_specific + envelope_selection: provider_specific + address_position: true + attachment_starts_on_new_sheet: true + supported_postal_products: + - standard + - provider_specific + limitations: + - Public Binect documentation confirms status queries but does not establish ordinary postal final-delivery proof for all products. + - Public documentation confirms validation and preview concepts but implementation must rely on the live Binect Swagger/API for exact field names. + - Idempotency should be enforced by hybridmail-connect if not provided natively by the Binect endpoint used. + - Cancellation semantics must be treated as provider-specific and time-sensitive. +``` + +## 6. Binect-Specific Conceptual Workflow + +The Binect flavor uses a document-centric workflow. + +```text +create/upload document +→ validate document +→ optionally add attachments +→ set or confirm print/shipping options +→ optionally retrieve preview +→ create/send sending +→ query document status +→ query shipping/sending status +→ receive or poll further status updates +``` + +The adapter MUST preserve this distinction: + +```text +Document preparation is not sending. +Sending is not postal handover. +Postal handover is not final physical delivery proof. +``` + +## 7. Binect Object Mapping + +## 7.1 Generic to Binect Mapping + +| Generic hybrid-mail concept | Binect flavor concept | +| --------------------------- | --------------------------------------------------------------------------- | +| `HybridmailLetter` | Letter-level abstraction composed from Binect document plus sending context | +| `HybridmailDocumentRef` | Binect document | +| `HybridmailAttempt` | Binect document upload and/or sending operation | +| `HybridmailBatch` | Binect serial/bulk document or sending group | +| `PostalRecipient` | Address extracted from document or provided as metadata if supported | +| `HybridmailOptions` | Binect print and shipping options | +| `HybridmailPreview` | Binect preview/brief view | +| `HybridmailTimeline` | Aggregated Binect document and shipping status history | + +## 7.2 BinectDocument + +```yaml +BinectDocument: + binect_document_id: string + hybridmail_letter_id: string? + coordination_case_id: string? + participant_id: string? + provider_status: string? + normalized_status: string + original_file_ref: ResourceRef + file_type: pdf | ps | unknown + page_count: integer? + sheet_count: integer? + price_estimate: MoneyAmount? + validation_result: BinectValidationResult? + preview_refs: + - BinectPreviewRef + attachment_refs: + - BinectAttachmentRef + created_at: timestamp + updated_at: timestamp? + metadata: object? +``` + +## 7.3 BinectAttachment + +```yaml +BinectAttachment: + binect_attachment_id: string + binect_document_id: string + attachment_file_ref: ResourceRef + file_type: pdf | rtf | txt | png | jpg | jpeg | bmp | gif | ps | unknown + file_size_bytes: integer? + starts_on_new_sheet: boolean? + provider_status: string? + validation_result: BinectValidationResult? + created_at: timestamp + metadata: object? +``` + +### Attachment Handling Rule + +The Binect flavor SHOULD default `starts_on_new_sheet` to `true` if no explicit instruction is provided, because the public FAQ states that attachments start on a new sheet by default. + +If duplex printing is used and the primary document has an odd number of pages, the adapter SHOULD be prepared for provider-side blank-page insertion semantics. Blank pages may affect sheet count and preview/status display, but should not be treated as billable unless the provider reports otherwise. + +## 7.4 BinectSending + +```yaml +BinectSending: + binect_sending_id: string + binect_document_id: string? + hybridmail_letter_id: string + coordination_case_id: string? + participant_id: string? + provider_status: string? + normalized_status: string + shipping_options: BinectShippingOptions? + print_options: BinectPrintOptions? + submitted_at: timestamp? + status_checked_at: timestamp? + child_document_statuses: + - BinectChildDocumentStatus + metadata: object? +``` + +## 7.5 BinectChildDocumentStatus + +For serial documents or bulk processing, child document statuses MUST be represented individually. + +```yaml +BinectChildDocumentStatus: + parent_document_id: string + child_document_id: string + participant_id: string? + provider_status: string? + normalized_status: string + evidence_event_refs: + - string + updated_at: timestamp? + metadata: object? +``` + +Batch-level success MUST NOT be treated as success for every child document unless each child status supports that interpretation. + +## 8. Binect Validation Model + +Binect validation results MUST be mapped into the generic `HybridmailValidationResult`. + +The public material indicates validation of: + +* address position +* address format +* restricted areas +* upload/print suitability + +```yaml +BinectValidationResult: + document_id: string + provider_validation_state: string? + validation_state: pending | passed | action_required | failed | unknown + issues: + - BinectValidationIssue + price_estimate: MoneyAmount? + validated_at: timestamp? + raw_provider_response_ref: string? +``` + +```yaml +BinectValidationIssue: + provider_issue_code: string? + issue_code: string + severity: info | warning | action_required | fatal + category: format | page_size | address | address_position | address_format | restricted_area | margins | file_size | page_count | attachment | postage | country | provider_policy | unknown + message: string + page_number: integer? + bounding_box: object? + fixable: boolean? +``` + +## 9. Binect Validation Issue Mapping + +| Binect validation concern | Generic validation category | Normalized issue code | +| ------------------------- | --------------------------- | --------------------------- | +| Address position invalid | `address_position` | `address_position_invalid` | +| Address format invalid | `address_format` | `address_format_invalid` | +| Restricted area violation | `restricted_area` | `restricted_area_violation` | +| Unsupported file type | `format` | `unsupported_file_type` | +| File too large | `file_size` | `file_too_large` | +| Attachment invalid | `attachment` | `attachment_invalid` | +| Postal option invalid | `postage` | `unsupported_product` | +| Provider policy rejection | `provider_policy` | `provider_policy_rejected` | +| Unknown validation issue | `unknown` | `unknown_validation_issue` | + +Validation failure MUST emit: + +```text +payload.validation_failed +``` + +Validation success MUST emit: + +```text +payload.validation_passed +``` + +Validation warnings MAY emit `payload.validation_passed` with warning metadata if the provider still allows sending. + +## 10. Binect Print and Shipping Options + +The Binect flavor MUST map Binect options into the generic `HybridmailOptions`. + +```yaml +BinectPrintOptions: + color_mode: color | grayscale | provider_default | unknown + print_sides: simplex | duplex | provider_default | unknown + attachment_starts_on_new_sheet: boolean? + paper_type: provider_specific? + metadata: object? +``` + +```yaml +BinectShippingOptions: + postage_product: standard | provider_specific + country_scope: domestic | international | unknown + dispatch_mode: manual_review | auto_send | submit_later | scheduled + desired_dispatch_date: date? + sender_profile_ref: string? + return_mail_handling: none | provider_processed | physical_return | digital_return_info | unknown + metadata: object? +``` + +Provider-native option names MUST be preserved in metadata. + +The normalized model MUST avoid assuming support for a product unless the live provider capability descriptor confirms it. + +## 11. Binect Preview Model + +Binect preview retrieval MUST map to `HybridmailPreview`. + +```yaml +BinectPreviewRef: + preview_id: string + binect_document_id: string + preview_type: pdf | png | image | metadata | unknown + preview_uri: string? + generated_at: timestamp + expires_at: timestamp? + raw_provider_response_ref: string? +``` + +Preview availability MUST emit an operational event, normally: + +```text +delivery.payload.available +``` + +Preview availability MUST NOT be interpreted as postal delivery evidence. + +## 12. Binect Status Model + +Because provider-native statuses may differ across endpoints, the Binect flavor MUST preserve raw provider status and map it to normalized status. + +```yaml +BinectStatusRecord: + status_record_id: string + binect_document_id: string? + binect_sending_id: string? + child_document_id: string? + provider_status: string + normalized_status: string + status_scope: document | sending | child_document | shipping | unknown + observed_at: timestamp + raw_provider_response_ref: string? +``` + +## 13. Normalized Binect Status Categories + +The Binect flavor SHOULD map native statuses into these normalized categories: + +```text +document.created +document.uploaded +document.validation_pending +document.validation_passed +document.validation_failed +document.preview_available +document.ready_for_sending +sending.submitted +sending.processing +sending.production_started +sending.production_completed +sending.handed_to_postal_service +sending.failed +sending.cancelled +sending.status_unknown +shipping.status_unknown +shipping.undeliverable +shipping.return_received +``` + +Where Binect status granularity is insufficient, the adapter MUST use the nearest weaker event and preserve the raw status in metadata. + +Example: + +```text +If the provider status only says "sent" and does not distinguish production completion from postal handover, +the adapter SHOULD map it conservatively and document the chosen interpretation. +``` + +## 14. Binect-to-Generic Event Mapping + +| Binect workflow event | Generic event | Evidence interpretation | +| --------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------- | +| Document upload accepted | `delivery.payload.accepted` | Digital submission accepted | +| Document rejected on upload | `delivery.payload.rejected` | Attempt failed before validation/send | +| Validation passed | `payload.validation_passed` | Document is provider-processable | +| Validation failed | `payload.validation_failed` | Document/action must be corrected | +| Price calculated on upload | metadata on validation/upload event | Cost signal, not delivery evidence | +| Attachment added | `delivery.payload.accepted` with attachment metadata | Attachment accepted into document context | +| Attachment rejected | `payload.validation_failed` | Attachment must be corrected | +| Preview generated | `delivery.payload.available` | Operational review evidence | +| Options set | metadata event or `delivery.payload.available` | Operational readiness | +| Sending submitted | `delivery.payload.submitted` | Provider instructed to send | +| Document/sending processing | `delivery.production.started` | Provider production chain began | +| Production completed | `delivery.production.completed` | Printed/enveloped/franked if provider status supports this | +| Handover to post | `delivery.postal.handed_over` | Strong dispatch evidence | +| Shipping status unknown | `delivery.postal.status_unknown` | Final delivery unclear | +| Undeliverable/returned | `delivery.postal.undeliverable` or `delivery.postal.return_received` | Strong negative evidence | +| Child document status | per-child mapped event | Required for serial/bulk cases | + +## 15. Evidence Grading Rules + +### 15.1 Document Upload Accepted + +```yaml +event_type: delivery.payload.accepted +evidence_grade: + strength: weak + actor_certainty: none + authority_certainty: none + payload_certainty: medium + interaction_certainty: none + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: low +notes: + - Binect accepted the document upload or document creation step. + - This does not prove validation, sending, production, postal handover, or delivery. +``` + +### 15.2 Binect Validation Passed + +```yaml +event_type: payload.validation_passed +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Document passed Binect validation for further processing. + - This does not prove sending or postal handover. +``` + +### 15.3 Binect Validation Failed + +```yaml +event_type: payload.validation_failed +evidence_grade: + strength: negative + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Document is not processable without correction or replacement. + - The validation issue category should be included. +``` + +### 15.4 Preview Available + +```yaml +event_type: delivery.payload.available +evidence_grade: + strength: weak + actor_certainty: none + authority_certainty: none + payload_certainty: medium + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Binect preview is available for review. + - Preview availability is operational evidence, not postal dispatch evidence. +``` + +### 15.5 Sending Submitted + +```yaml +event_type: delivery.payload.submitted +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Binect sending was submitted. + - This does not prove production completion or postal handover. +``` + +### 15.6 Production Started + +```yaml +event_type: delivery.production.started +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: medium + channel_certainty: high + non_repudiation_strength: low +notes: + - Binect status indicates processing/production has started. + - The exact native status should be preserved in metadata. +``` + +### 15.7 Postal Handover + +```yaml +event_type: delivery.postal.handed_over +evidence_grade: + strength: strong + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - Binect status indicates handover into the physical postal delivery chain. + - This is strong dispatch evidence. + - It does not prove recipient receipt or reading. +``` + +### 15.8 Return or Undeliverable + +```yaml +event_type: delivery.postal.return_received +evidence_grade: + strength: negative + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - Binect or downstream status indicates the physical letter was returned or undeliverable. + - This is strong negative delivery evidence. +``` + +## 16. Binect Evidence Assessment + +The Binect flavor SHOULD provide a Binect-native evidence assessment. + +```yaml +BinectEvidenceAssessment: + hybridmail_letter_id: string + binect_document_id: string? + binect_sending_id: string? + participant_id: string? + category: success | fail | undef + subclass: string + confidence: low | medium | high + strongest_signal: string? + evidence_summary: + - string + recommended_coordination_interpretation: string? +``` + +### 16.1 Binect Adapter Success Subclasses + +```text +success.document_uploaded +success.validation_passed +success.preview_available +success.sending_submitted +success.production_started +success.production_completed +success.handed_to_postal_service +success.return_info_processed +``` + +### 16.2 Binect Adapter Fail Subclasses + +```text +fail.upload_rejected +fail.validation_failed +fail.address_position_invalid +fail.address_format_invalid +fail.restricted_area_violation +fail.attachment_invalid +fail.unsupported_file_type +fail.file_too_large +fail.options_invalid +fail.sending_rejected +fail.production_failed +fail.cancelled +fail.return_received +fail.undeliverable +``` + +### 16.3 Binect Adapter Undef Subclasses + +```text +undef.pending +undef.uploaded_only +undef.validation_pending +undef.ready_but_not_sent +undef.preview_available_only +undef.submitted_for_sending +undef.processing +undef.production_status_unknown +undef.handed_to_postal_service_but_delivery_unproven +undef.shipping_status_unknown +undef.no_final_status_expected +undef.child_status_partial +undef.conflicting_evidence +undef.channel_degraded +``` + +## 17. Binect Workflow States + +The Binect flavor SHOULD support these normalized workflow states: + +```text +created +upload_requested +uploaded +upload_failed +validation_pending +validation_passed +validation_failed +attachment_pending +attachment_added +attachment_failed +options_pending +options_set +preview_pending +preview_available +ready_for_sending +send_requested +sending_submitted +processing +production_started +production_completed +handed_to_postal_service +shipping_status_unknown +return_received +undeliverable +cancelled +failed +unknown +``` + +These states are Binect-flavor states, not coordination result states. + +## 18. Binect Idempotency and Duplicate Send Prevention + +Duplicate physical send prevention is mandatory. + +If the Binect endpoint used does not provide native idempotency, `hybridmail-connect` MUST implement adapter-managed idempotency. + +Idempotency scope SHOULD include: + +```text +coordination_case_id +participant_id +delivery_id +payload_id +payload_integrity_hash +provider_flavor +target postal recipient +shipping options +``` + +A repeated `delivery.submit_letter` request with the same idempotency key MUST NOT create a second physical send. + +If the repeated request refers to a changed payload or changed address, the adapter MUST reject it as an idempotency conflict unless a new idempotency key is provided. + +## 19. Binect Attachments + +The Binect flavor MUST support attachments according to provider capability. + +Supported attachment formats from public FAQ: + +```text +pdf +rtf +txt +png +jpg +jpeg +bmp +gif +ps +``` + +Maximum attachment size from public FAQ: + +```text +20 MB +``` + +The adapter SHOULD validate attachment file type and size before submission where possible. + +Attachment events SHOULD be mapped as: + +| Attachment event | Generic event | +| ----------------------------- | ---------------------------------------------------- | +| Attachment accepted | `delivery.payload.accepted` with attachment metadata | +| Attachment rejected | `payload.validation_failed` | +| Attachment validation warning | `payload.validation_passed` with warning metadata | + +## 20. Binect Cost and Price Handling + +The public Binect FAQ states that price calculation occurs when uploading the letter. + +The Binect flavor SHOULD capture provider cost information when available. + +```yaml +BinectCostEstimate: + binect_document_id: string + amount_net: number? + amount_gross: number? + currency: string? + tax_amount: number? + page_count: integer? + sheet_count: integer? + postage_product: string? + calculated_at: timestamp? + raw_provider_response_ref: string? +``` + +Cost calculation is operational metadata and MUST NOT be treated as delivery evidence. + +## 21. Binect Status Polling and Webhooks + +The Binect flavor MUST support polling if status endpoints are available. + +The Binect flavor MAY support webhooks if available in the provider configuration. + +```yaml +BinectStatusPollingConfig: + enabled: boolean + initial_delay_seconds: integer + interval_seconds: integer + max_duration_seconds: integer + terminal_statuses: + - string +``` + +Polling MUST be idempotent and MUST preserve status history. + +Late status changes MUST be recorded. + +## 22. Binect Raw Event Preservation + +The Binect flavor SHOULD preserve raw provider responses or references to them. + +```yaml +RawBinectEventRef: + raw_event_id: string + source: api_response | status_poll | webhook | preview | validation | operator + endpoint: string? + storage_ref: string? + received_at: timestamp + redacted: boolean +``` + +Normalized events SHOULD reference raw Binect event data where available. + +## 23. Binect Channel Health + +```yaml +BinectChannelHealth: + provider_name: binect + provider_account_ref: string? + status: healthy | degraded | failing | unknown + authentication_status: valid | expired | missing | insufficient | unknown + upload_status: healthy | degraded | unavailable | unknown + validation_status: healthy | degraded | unavailable | unknown + preview_status: healthy | degraded | unavailable | unknown + sending_status: healthy | delayed | unavailable | unknown + status_api_status: healthy | degraded | unavailable | unknown + cutoff_status: before_cutoff | after_cutoff | no_production_day | unknown + known_degradations: + - string +``` + +Health events SHOULD map to: + +```text +system.provider.degraded +system.provider.unavailable +system.adapter.health_changed +``` + +## 24. Security Requirements + +The Binect flavor MUST: + +* protect Binect API credentials. +* use secure transport. +* avoid logging document contents. +* protect postal recipient data. +* preserve idempotency. +* prevent duplicate physical sends. +* separate test and production configuration. +* protect previews, because previews may contain full document content. +* validate file type and size before upload where possible. +* apply tenant/account separation where applicable. + +## 25. Privacy Requirements + +The Binect flavor SHOULD: + +* store payload references instead of payload content where possible. +* support metadata-only mode after provider submission. +* mask postal address data in logs. +* support configurable retention of raw provider responses. +* support configurable retention of previews. +* separate operational diagnostics from coordination evidence. +* document provider-side retention limitations. +* support deletion or anonymization workflows where legally possible. + +## 26. Reliability Requirements + +The Binect flavor MUST support: + +* idempotent submit requests. +* duplicate status event detection. +* out-of-order status handling. +* polling-based status recovery. +* retryable upload failures. +* non-retryable validation failures. +* provider timeout handling. +* validation issue preservation. +* preview retrieval failure handling. +* child-document partial-status handling. +* correlation preservation. +* dead-letter handling for unprocessable provider responses. + +## 27. Minimal API Surface + +The Binect flavor SHOULD implement or expose these conceptual operations through `hybridmail-connect`. + +### 27.1 Adapter Contract Operations + +```text +GET /adapter/descriptor +GET /adapter/health +POST /adapter/actions +POST /adapter/events/provider +GET /adapter/events +GET /adapter/letters/{id}/timeline +GET /adapter/letters/{id}/assessment +``` + +### 27.2 Binect Flavor Operations + +```text +POST /binect/documents +POST /binect/documents/{id}/attachments +PUT /binect/documents/{id}/options +GET /binect/documents/{id}/preview +POST /binect/sendings +GET /binect/documents/{id}/status +GET /binect/sendings/{id}/status +GET /binect/letters/{id}/timeline +GET /binect/letters/{id}/assessment +GET /binect/channel-health +``` + +The exact provider endpoint names MUST be implemented according to the live Binect Swagger/API. The conceptual operations above define the adapter-facing semantic model. + +## 28. Example End-to-End Flow + +### 28.1 Single Binect Letter + +1. `coordination-engine` creates a coordination case. +2. A PDF payload is registered. +3. Policy selects `hybridmail-connect` with provider flavor `binect`. +4. `coordination-engine` sends `delivery.submit_letter`. +5. `hybridmail-connect` creates a Binect document upload operation. +6. Binect accepts the document. +7. The adapter emits `delivery.payload.accepted`. +8. Binect validates the document. +9. If validation passes, the adapter emits `payload.validation_passed`. +10. Optional attachments are uploaded. +11. Print and shipping options are set or confirmed. +12. A preview is requested if policy requires review. +13. The adapter emits `delivery.payload.available` for preview availability. +14. The sending is submitted. +15. The adapter emits `delivery.payload.submitted`. +16. Status polling reports processing/production. +17. The adapter emits `delivery.production.started` and later stronger production or postal events if supported. +18. If status indicates postal handover, the adapter emits `delivery.postal.handed_over`. +19. `coordination-engine` evaluates whether handover is sufficient for the case policy. + +### 28.2 Validation Failure + +1. Binect rejects the document due to address position, address format, or restricted-area violation. +2. The adapter emits `payload.validation_failed`. +3. The validation issue is preserved with category and severity. +4. `coordination-engine` marks the participant delivery as action-required or failed for the attempt. +5. Policy requests correction, alternate generation, or fallback channel. + +### 28.3 Serial / Bulk Document + +1. A serial or bulk document is submitted. +2. Binect returns parent and child statuses. +3. The adapter records parent status. +4. The adapter creates or updates `BinectChildDocumentStatus` records. +5. The adapter emits per-child evidence events. +6. `coordination-engine` aggregates participant-level completion and does not treat parent-level acceptance as universal delivery success. + +## 29. Binect MVP Scope + +The first Binect flavor implementation should include: + +1. Binect provider flavor descriptor. +2. Adapter descriptor integration. +3. Binect credential/config handling. +4. Document upload. +5. Attachment upload. +6. Print/shipping option setting. +7. Preview retrieval. +8. Sending submission. +9. Document status polling. +10. Sending/shipping status polling. +11. Validation result mapping. +12. Child document status mapping where exposed. +13. Evidence event generation. +14. Letter timeline. +15. Binect evidence assessment. +16. Adapter-managed idempotency. + +### MVP Required Binect Events + +```text +delivery.payload.accepted +delivery.payload.rejected +payload.validation_passed +payload.validation_failed +delivery.payload.available +delivery.payload.submitted +delivery.production.started +delivery.production.completed +delivery.postal.handed_over +delivery.postal.status_unknown +delivery.postal.undeliverable +delivery.postal.return_received +system.provider.degraded +system.provider.unavailable +``` + +Where a provider status is unavailable or not semantically clear, the adapter MUST emit the weakest safe event and preserve the raw status. + +## 30. Binect MVP Acceptance Criteria + +The Binect flavor MVP is acceptable when it can: + +1. Accept a coordination-compatible `delivery.submit_letter` request. +2. Upload or simulate upload of a Binect document. +3. Preserve correlation and idempotency. +4. Prevent duplicate physical sends for duplicate idempotency keys. +5. Add supported attachments. +6. Set or preserve print/shipping options. +7. Retrieve or simulate a preview. +8. Submit sending. +9. Poll and normalize document status. +10. Poll and normalize sending/shipping status. +11. Map validation success to `payload.validation_passed`. +12. Map address/layout validation failure to `payload.validation_failed`. +13. Map sending submission to `delivery.payload.submitted`. +14. Map postal handover only when semantically supported by provider status. +15. Provide a Binect letter timeline. +16. Provide a Binect evidence assessment. +17. Integrate with `coordination-engine` without overclaiming physical delivery or human awareness. + +## 31. Open Questions + +1. Which exact Binect native statuses should map to `delivery.production.started`, `delivery.production.completed`, and `delivery.postal.handed_over`? +2. Does the relevant Binect account/API configuration expose webhooks, or is polling the required baseline? +3. Which postal products are available for the target Binect customer/account? +4. Which cancellation states are possible after upload but before sending or production? +5. Which fields in the live Swagger/API represent child-document status for serial documents? +6. How should provider price calculation be represented when a document is uploaded but not yet sent? +7. Which preview formats are available in the target environment? +8. Are return-mail or undeliverable events available through API status, manual import, or a separate process? +9. Are registered-mail or delivery-confirmation products available in the target Binect configuration? +10. Should Binect-specific validation issues update a central contact/address quality registry? + +## 32. Non-Goals + +The Binect flavor is not: + +* a document authoring system. +* a PDF renderer by default. +* a legal notice system by itself. +* a postal carrier. +* a general Binect UI replacement. +* the owner of coordination case success. +* the owner of contract, payment, or signature result semantics. + +It integrates Binect hybrid-mail capabilities into the coordination framework. + +## 33. Summary + +`HybridmailBinectSpecification.md` defines the Binect provider flavor for `hybridmail-connect`. + +The Binect flavor models Binect as a document-centric hybrid-mail provider with: + +* document upload +* validation +* attachments +* print and shipping options +* preview +* sending +* document and shipping status +* serial/child document status +* production and postal evidence mapping + +The key rule is: + +> Binect events are provider, document, production, and postal-chain evidence. They are not automatic coordination result satisfaction. hybridmail-connect reports Binect-channel facts and uncertainty. coordination-engine evaluates intended results. + diff --git a/spec/HybridmailEpostbusinessSpecification.md b/spec/HybridmailEpostbusinessSpecification.md new file mode 100644 index 0000000..491571b --- /dev/null +++ b/spec/HybridmailEpostbusinessSpecification.md @@ -0,0 +1,1220 @@ +# HybridmailDpagSpecification.md + +## 1. Document Status + +**Document:** HybridmailDpagSpecification.md +**Project:** hybridmail-connect +**Provider Flavor:** Deutsche Post / E-POSTBUSINESS API +**Parent Specification:** HybridmailAdapterSpecification.md +**Target Integration:** coordination-engine +**Adapter Contract:** AdapterInterfaceSpecification.md v1.0 +**Status:** Draft v1.0 +**Primary Scope:** Deutsche Post / E-POSTBUSINESS hybrid-mail provider flavor for PDF mail-shipment submission, one-by-one and bulk upload, validation, address-window positioning, cover-sheet handling, scheduled dispatch, production status, shipment tracking, registered-mail products, and delivery/return evidence mapping. + +## 2. Purpose + +This document specifies the Deutsche Post / E-POSTBUSINESS API provider flavor for `hybridmail-connect`. + +The DPAG flavor implements the generic hybrid-mail adapter model for the Deutsche Post E-POSTBUSINESS API / Deutsche Post Hybrid Mail Shipments E-POST API family. It maps Deutsche Post’s hybrid-mail API concepts into the provider-neutral hybrid-mail model used by `coordination-engine`. + +The purpose of this document is to capture: + +* DPAG/E-POSTBUSINESS-specific provider capabilities. +* DPAG-specific workflow stages. +* PDF mail-shipment submission. +* Single and bulk upload handling. +* Production and shipment-status mapping. +* Address-window positioning and automatic positioning semantics. +* Individual cover-sheet support where available. +* Scheduled dispatch semantics. +* Registered-mail and delivery-tracking semantics. +* PDF/PDF-A requirements and validation. +* DPAG-specific evidence event mapping. +* DPAG-specific limitations and assumptions. +* DPAG-specific MVP boundaries. + +The DPAG flavor MUST remain compatible with `HybridmailAdapterSpecification.md` and MUST NOT leak DPAG-specific terminology into the core coordination model except through mapped provider metadata. + +## 3. Grounding Summary + +The public Deutsche Post / DHL developer and product material indicates the following relevant features: + +* The Deutsche Post Hybrid Mail Shipments E-POST API is intended for business customers sending PDF documents as mail shipments with Deutsche Post. +* The API expects single PDF documents for mail shipments. +* It supports one-by-one and bulk upload. +* It provides detailed shipment processing information. +* It supports bulk status requests. +* It provides focused information for suppliers. +* It supports varied mail shipments of Deutsche Post with E-POST. +* It supports tracking the shipment from uploading through delivery. +* The broader E-POSTBUSINESS API is described as a REST API with Swagger documentation. +* It offers detailed shipment-level status information, including production data and shipment tracking. +* It supports individual submission through mass submission. +* It includes address-window positioning aids, automatic positioning, individual cover sheets, and scheduled dispatch. +* It includes hybrid dispatch services where digital documents are printed, enveloped, franked, and delivered as physical letters. +* Registered-letter use cases are part of the public API/product description. +* PDF/A-1b knowledge is explicitly relevant in public API notes. +* Activation, business-software partner setup, API access data, and technical login/access handling are part of the adoption model. + +These facts define the baseline for this provider flavor. + +## 4. Provider Flavor Identity + +```yaml id="7qj3nr" +HybridmailProviderFlavorDescriptor: + provider_name: dpag_epost + provider_label: Deutsche Post E-POSTBUSINESS API / Hybrid Mail Shipments E-POST + provider_family: hybridmail + adapter_contract_version: "1.0" + parent_specification: HybridmailAdapterSpecification.md + deployment_mode: external +``` + +## 5. DPAG Capability Descriptor + +The DPAG flavor MUST expose a capability descriptor. + +```yaml id="bs763g" +HybridmailProviderFlavorDescriptor: + provider_name: dpag_epost + provider_version: provider_specific + supported_file_types: + - pdf + - pdfa + supported_attachment_file_types: + - provider_specific + supports_single_letter: true + supports_bulk: true + supports_serial_letters: true + supports_attachments: provider_specific + supports_preview: provider_specific + supports_webhooks: provider_specific + supports_polling: true + supports_bulk_status_requests: true + supports_supplier_focused_status: true + supports_registered_mail: true + supports_delivery_confirmation: product_dependent + supports_return_mail: provider_specific + supports_address_correction: provider_specific + supports_international_mail: true + supports_cancellation: provider_specific + supports_staging: true + supports_idempotency: adapter_managed_or_provider_specific + supports_rate_limits: true + supports_scheduled_dispatch: true + supports_address_window_positioning: true + supports_automatic_positioning: true + supports_cover_sheet: true + supports_pdfa_validation: true + supports_production_status: true + supports_shipment_tracking: true + supports_gogreen_plus: true + supported_print_options: + color_mode: provider_specific + simplex_duplex: provider_specific + envelope_selection: provider_specific + address_position: true + automatic_positioning: true + cover_sheet: true + supported_postal_products: + - standard + - registered + - registered_return_receipt + - delivery_confirmation + - international + - provider_specific + limitations: + - Public documentation confirms upload-to-delivery tracking, but exact native status names and semantics must be implemented against the active API documentation and account configuration. + - Registered-mail and delivery-confirmation evidence are product-dependent. + - Ordinary letters may provide strong production and postal-chain evidence without proving human awareness. + - PDF/PDF-A and address-window constraints must be treated as provider-specific validation concerns. + - Idempotency should be adapter-managed unless the active API endpoint provides native idempotency semantics. + - Cancellation semantics are provider-specific and likely time-sensitive. +``` + +## 6. DPAG-Specific Conceptual Workflow + +The DPAG flavor uses a PDF mail-shipment workflow. + +```text id="reoa08" +authenticate / obtain access +→ prepare PDF or PDF/A document +→ submit single or bulk mail shipment +→ validate PDF, address, product, and layout constraints +→ optionally apply address-window positioning / automatic positioning / cover sheet +→ optionally schedule dispatch +→ provider accepts shipment +→ production processing +→ print / envelope / frank +→ postal handover +→ shipment tracking / registered-mail tracking where supported +→ delivery, return, or unknown final status depending on product and evidence +``` + +The adapter MUST preserve this distinction: + +```text id="ypwf4z" +PDF upload is not validation success. +Validation success is not sending. +Sending/production is not postal handover. +Postal handover is not necessarily final delivery confirmation. +Delivery tracking is product- and status-dependent. +``` + +## 7. DPAG Object Mapping + +## 7.1 Generic to DPAG Mapping + +| Generic hybrid-mail concept | DPAG flavor concept | +| --------------------------- | ------------------------------------------------------------------------------------------------------ | +| `HybridmailLetter` | DPAG mail shipment / E-POST shipment | +| `HybridmailDocumentRef` | PDF/PDF-A document submitted for mail shipment | +| `HybridmailAttempt` | DPAG upload/submission/status operation | +| `HybridmailBatch` | Bulk upload / mass submission | +| `PostalRecipient` | Address extracted from PDF, positioned in address window, or provided through metadata where supported | +| `HybridmailOptions` | DPAG shipment, product, print, positioning, scheduling, and cover-sheet options | +| `HybridmailPreview` | Provider-specific preview or validation representation if supported | +| `HybridmailTimeline` | DPAG upload, production, shipment, tracking, and return timeline | + +## 7.2 DpagShipment + +```yaml id="5wcgbn" +DpagShipment: + dpag_shipment_id: string + hybridmail_letter_id: string? + coordination_case_id: string? + participant_id: string? + provider_status: string? + normalized_status: string + document_ref: DpagDocumentRef + recipient: PostalRecipient? + sender: PostalSender? + options: DpagShipmentOptions? + production_data_ref: string? + tracking_ref: string? + registered_mail_ref: string? + created_at: timestamp + updated_at: timestamp? + metadata: object? +``` + +## 7.3 DpagDocumentRef + +```yaml id="c50301" +DpagDocumentRef: + dpag_document_id: string? + hybridmail_document_id: string + original_file_ref: ResourceRef + document_type: pdf | pdfa | unknown + pdfa_profile: pdfa_1b | provider_specific | none | unknown + file_size_bytes: integer? + page_count: integer? + sheet_count: integer? + integrity_hash: string? + validation_result: DpagValidationResult? + created_at: timestamp + metadata: object? +``` + +## 7.4 DpagSubmissionAttempt + +```yaml id="ps6n1n" +DpagSubmissionAttempt: + dpag_attempt_id: string + hybridmail_letter_id: string? + hybridmail_batch_id: string? + provider_operation_id: string? + idempotency_key: string + operation_type: upload_single | upload_bulk | validate | submit | status_poll | bulk_status_poll | cancel | tracking_poll + state: pending | accepted | rejected | failed | completed | duplicate | unknown + created_at: timestamp + updated_at: timestamp? + raw_provider_response_ref: string? +``` + +## 7.5 DpagBatch + +```yaml id="k7g0d2" +DpagBatch: + dpag_batch_id: string? + hybridmail_batch_id: string + coordination_case_id: string? + batch_type: bulk | mass_submission | serial | mixed | unknown + shipments: + - dpag_shipment_id + state: pending | partial | completed | failed | unknown + created_at: timestamp + updated_at: timestamp? + bulk_status_ref: string? +``` + +Batch-level status MUST NOT be treated as success for every child shipment unless DPAG status data provides per-shipment evidence or scenario policy explicitly accepts batch-level evidence. + +## 8. DPAG Address and Positioning Model + +Public material explicitly mentions address-window positioning aids and automatic positioning. The DPAG flavor MUST model address positioning as a first-class provider concern. + +```yaml id="aob25f" +DpagAddressPositioning: + mode: fixed_window | automatic_positioning | cover_sheet | metadata | provider_default | unknown + window_position: left | right | provider_specific | unknown + page_number: integer? + bounding_box: object? + provider_positioning_result: string? + confidence: low | medium | high +``` + +Generic postal recipient mapping: + +```yaml id="ew9cwa" +PostalRecipient: + recipient_id: string? + name_lines: + - string + organization: string? + street: string? + house_number: string? + address_addition: string? + postal_code: string? + city: string? + region: string? + country_code: string? + country_name: string? + address_source: extracted_from_document | provided_metadata | provider_detected | cover_sheet | manual | unknown + address_quality: PostalAddressQuality? +``` + +If automatic positioning or cover-sheet generation changes where the address appears in the physical output, the adapter MUST preserve that fact in metadata. + +## 9. DPAG Cover Sheet Model + +Public material references individual cover sheets. The adapter SHOULD model cover-sheet use explicitly. + +```yaml id="c5w0y6" +DpagCoverSheet: + enabled: boolean + cover_sheet_type: individual | provider_default | provider_specific | unknown + recipient_source: metadata | extracted | provided | unknown + sender_source: metadata | provided | provider_default | unknown + template_ref: string? + generated_document_ref: ResourceRef? + metadata: object? +``` + +Cover-sheet generation is a production preparation step. It does not prove physical dispatch. + +## 10. DPAG Scheduled Dispatch Model + +Public material references scheduled dispatch. The adapter SHOULD model dispatch timing. + +```yaml id="zr33sn" +DpagScheduledDispatch: + dispatch_mode: immediate | scheduled | provider_default | unknown + desired_dispatch_date: date? + desired_dispatch_time_window: string? + schedule_status: not_scheduled | scheduled | missed | cancelled | completed | unknown + metadata: object? +``` + +Scheduled dispatch affects policy interpretation: + +```text id="8bh4mo" +A letter scheduled for future dispatch should remain pending until the scheduled production/dispatch window begins. +``` + +## 11. DPAG Shipment Options + +The DPAG flavor MUST map DPAG options into generic `HybridmailOptions`. + +```yaml id="2lo1jt" +DpagShipmentOptions: + color_mode: color | grayscale | provider_default | unknown + print_sides: simplex | duplex | provider_default | unknown + envelope_type: c5 | c4 | window_left | window_right | provider_default | unknown + address_positioning: DpagAddressPositioning? + cover_sheet: DpagCoverSheet? + postage_product: standard | registered | registered_return_receipt | delivery_confirmation | international | provider_specific + country_scope: domestic | international | unknown + scheduled_dispatch: DpagScheduledDispatch? + return_mail_handling: none | provider_processed | physical_return | digital_return_info | unknown + gogreen_plus: boolean? + metadata: object? +``` + +Provider-native option names MUST be preserved in metadata. + +The adapter MUST NOT assume support for a product unless the DPAG capability descriptor confirms it for the active account/API context. + +## 12. DPAG Validation Model + +DPAG validation results MUST be mapped into generic `HybridmailValidationResult`. + +```yaml id="y66riu" +DpagValidationResult: + dpag_shipment_id: string? + dpag_document_id: string? + provider_validation_state: string? + validation_state: pending | passed | action_required | failed | unknown + issues: + - DpagValidationIssue + validated_at: timestamp? + raw_provider_response_ref: string? +``` + +```yaml id="ucmvxb" +DpagValidationIssue: + provider_issue_code: string? + issue_code: string + severity: info | warning | action_required | fatal + category: format | pdf | pdfa | page_size | address | address_position | restricted_area | cover_sheet | margins | file_size | page_count | postage | country | scheduled_dispatch | provider_policy | unknown + message: string + page_number: integer? + bounding_box: object? + fixable: boolean? +``` + +## 13. DPAG Validation Issue Mapping + +| DPAG validation concern | Generic validation category | Normalized issue code | +| ----------------------------------- | ----------------------------- | ---------------------------------- | +| Invalid PDF | `pdf` / `format` | `invalid_pdf` | +| PDF/A-1b issue | `pdfa` | `invalid_pdfa_1b` | +| Unsupported file type | `format` | `unsupported_file_type` | +| Page or file constraint violated | `page_count` / `file_size` | `document_constraint_violation` | +| Address not detected | `address` | `address_not_detected` | +| Address-window problem | `address_position` | `address_position_invalid` | +| Automatic positioning failed | `address_position` | `automatic_positioning_failed` | +| Cover sheet generation failed | `cover_sheet` | `cover_sheet_failed` | +| Restricted-area or margin violation | `restricted_area` / `margins` | `layout_restricted_area_violation` | +| Unsupported destination country | `country` | `unsupported_country` | +| Postal product invalid | `postage` | `unsupported_product` | +| Scheduled dispatch invalid | `scheduled_dispatch` | `scheduled_dispatch_invalid` | +| Provider policy rejection | `provider_policy` | `provider_policy_rejected` | +| Unknown validation issue | `unknown` | `unknown_validation_issue` | + +Validation failure MUST emit: + +```text id="wrwvgy" +payload.validation_failed +``` + +Validation success MUST emit: + +```text id="ybc5gu" +payload.validation_passed +``` + +Warnings MAY allow processing to continue, but the warning MUST be preserved in metadata. + +## 14. DPAG Status Model + +The DPAG flavor MUST preserve raw provider status and map it to normalized status. + +```yaml id="5o3jui" +DpagStatusRecord: + status_record_id: string + dpag_shipment_id: string? + dpag_batch_id: string? + provider_status: string + normalized_status: string + status_scope: upload | validation | production | shipment | tracking | registered_mail | bulk | supplier | unknown + observed_at: timestamp + raw_provider_response_ref: string? +``` + +## 15. Normalized DPAG Status Categories + +The DPAG flavor SHOULD map native statuses into these normalized categories: + +```text id="k3jagx" +shipment.created +shipment.uploaded +shipment.validation_pending +shipment.validation_passed +shipment.validation_failed +shipment.action_required +shipment.ready_for_sending +shipment.scheduled +shipment.submitted_for_sending +shipment.processing +shipment.production_started +shipment.production_completed +shipment.handed_to_postal_service +shipment.in_transit +shipment.delivery_confirmed +shipment.registered_event +shipment.undeliverable +shipment.return_received +shipment.cancelled +shipment.failed +shipment.status_unknown +``` + +Where DPAG status granularity is insufficient, the adapter MUST use the nearest weaker event and preserve raw status in metadata. + +## 16. DPAG Production Data Model + +Public documentation references production data. The adapter SHOULD model production-stage evidence separately from postal-stage evidence. + +```yaml id="oia4p4" +DpagProductionData: + dpag_shipment_id: string + production_status: pending | started | completed | failed | unknown + print_status: pending | printed | failed | unknown + enveloping_status: pending | enveloped | failed | unknown + franking_status: pending | franked | failed | unknown + production_site_ref: string? + production_started_at: timestamp? + production_completed_at: timestamp? + raw_provider_response_ref: string? +``` + +Production completion does not prove postal handover unless the provider status explicitly supports that interpretation. + +## 17. DPAG Tracking Model + +Public documentation references shipment tracking from upload through delivery. The adapter SHOULD model tracking as a separate status stream. + +```yaml id="ry1ic0" +DpagTrackingEvent: + tracking_event_id: string + dpag_shipment_id: string + provider_tracking_id: string? + tracking_type: production | postal | registered_mail | delivery_confirmation | return | unknown + provider_status: string + normalized_status: string + occurred_at: timestamp? + observed_at: timestamp + raw_provider_response_ref: string? + metadata: object? +``` + +Tracking events MUST be mapped according to their actual semantics. + +For ordinary mail, a tracking stage may indicate process progress without proving delivery. + +For registered mail or delivery-confirmation products, tracking events may provide stronger physical-delivery evidence. + +## 18. DPAG Registered Mail Model + +The public API material references registered-letter use cases and varied mail shipments. + +```yaml id="7r9xqo" +DpagRegisteredMail: + dpag_shipment_id: string + registered_product_type: registered | registered_return_receipt | delivery_confirmation | provider_specific + registered_tracking_id: string? + status: created | in_transit | delivery_attempted | delivered | refused | returned | undeliverable | unknown + delivery_confirmation_ref: ResourceRef? + recipient_signature_ref: ResourceRef? + occurred_at: timestamp? + observed_at: timestamp? + raw_provider_response_ref: string? +``` + +The adapter MUST only emit: + +```text id="3sgj7j" +delivery.postal.delivery_confirmed +``` + +when the DPAG status and selected postal product semantically support delivery confirmation. + +Ordinary letter dispatch MUST NOT be mapped to `delivery.postal.delivery_confirmed`. + +## 19. DPAG Bulk Status Model + +The API supports bulk upload and bulk status requests. The adapter MUST support per-shipment state under a batch. + +```yaml id="43ernq" +DpagBulkStatus: + dpag_batch_id: string + status_observed_at: timestamp + batch_status: pending | partial | completed | failed | unknown + child_statuses: + - DpagChildShipmentStatus + raw_provider_response_ref: string? +``` + +```yaml id="62finx" +DpagChildShipmentStatus: + dpag_batch_id: string + dpag_shipment_id: string + hybridmail_letter_id: string? + participant_id: string? + provider_status: string + normalized_status: string + evidence_event_refs: + - string + updated_at: timestamp? + metadata: object? +``` + +Batch-level acceptance MUST NOT be treated as per-recipient success without child-shipment evidence. + +## 20. DPAG-to-Generic Event Mapping + +| DPAG workflow event | Generic event | Evidence interpretation | +| ----------------------------- | ------------------------------------------------------------------------------------- | ----------------------------------- | +| PDF upload accepted | `delivery.payload.accepted` | Digital submission accepted | +| PDF upload rejected | `delivery.payload.rejected` | Attempt failed | +| Validation passed | `payload.validation_passed` | Document is provider-processable | +| Validation failed | `payload.validation_failed` | Document/action must be corrected | +| Automatic positioning applied | `delivery.payload.available` with positioning metadata | Operational readiness, not dispatch | +| Cover sheet generated | `delivery.payload.available` with cover-sheet metadata | Operational readiness | +| Scheduled dispatch accepted | `delivery.payload.available` with schedule metadata | Future dispatch pending | +| Submitted for sending | `delivery.payload.submitted` | Provider instructed to send | +| Production started | `delivery.production.started` | Provider production began | +| Production completed | `delivery.production.completed` | Physical production completed | +| Handed to postal service | `delivery.postal.handed_over` | Strong dispatch evidence | +| Tracking in transit | `delivery.postal.in_transit` | Postal tracking evidence | +| Registered tracking event | `delivery.postal.in_transit` or `delivery.postal.delivery_confirmed` depending status | Product-dependent | +| Delivery confirmed | `delivery.postal.delivery_confirmed` | Strong physical-delivery evidence | +| Undeliverable | `delivery.postal.undeliverable` | Strong negative delivery evidence | +| Return received | `delivery.postal.return_received` | Strong negative evidence | +| Bulk status child failed | child-level event | Per-recipient failure | +| Bulk status child completed | child-level mapped event | Per-recipient progress | + +## 21. Evidence Grading Rules + +### 21.1 DPAG PDF Upload Accepted + +```yaml id="ms003r" +event_type: delivery.payload.accepted +evidence_grade: + strength: weak + actor_certainty: none + authority_certainty: none + payload_certainty: medium + interaction_certainty: none + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: low +notes: + - DPAG accepted the digital PDF upload into the API workflow. + - This does not prove validation, production, postal handover, or delivery. +``` + +### 21.2 DPAG Validation Passed + +```yaml id="3qhzvf" +event_type: payload.validation_passed +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - DPAG validation passed or the shipment is processable according to provider status. + - This does not prove sending or postal handover. +``` + +### 21.3 DPAG Validation Failed + +```yaml id="jt0dxl" +event_type: payload.validation_failed +evidence_grade: + strength: negative + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - DPAG reported a document, address, layout, PDF/PDF-A, product, or policy issue. + - The issue should be categorized and preserved. +``` + +### 21.4 DPAG Production Started + +```yaml id="24mb0j" +event_type: delivery.production.started +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: medium + channel_certainty: high + non_repudiation_strength: low +notes: + - DPAG status indicates production processing started. + - The exact native status should be preserved in metadata. +``` + +### 21.5 DPAG Production Completed + +```yaml id="5qyv09" +event_type: delivery.production.completed +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - DPAG production status indicates physical production completed. + - This does not prove postal handover unless the provider status explicitly says so. +``` + +### 21.6 DPAG Postal Handover + +```yaml id="xqenak" +event_type: delivery.postal.handed_over +evidence_grade: + strength: strong + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - DPAG status indicates handover into the physical postal delivery chain. + - This is strong dispatch evidence. + - It does not prove recipient receipt or reading. +``` + +### 21.7 DPAG Delivery Confirmed / Registered Event + +```yaml id="aqs8rv" +event_type: delivery.postal.delivery_confirmed +evidence_grade: + strength: strong + actor_certainty: low + authority_certainty: none + payload_certainty: high + interaction_certainty: low + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - DPAG registered-mail or delivery-confirmation evidence indicates physical delivery. + - Product and native status semantics must support this mapping. + - This may still not prove that the intended human personally read the payload. +``` + +### 21.8 DPAG Undeliverable / Return + +```yaml id="8d68n9" +event_type: delivery.postal.return_received +evidence_grade: + strength: negative + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - DPAG or postal-chain status indicates undeliverable or returned mail. + - This is strong negative evidence for the physical delivery attempt. +``` + +## 22. DPAG Evidence Assessment + +The DPAG flavor SHOULD provide a DPAG-native evidence assessment. + +```yaml id="hsl1jn" +DpagEvidenceAssessment: + hybridmail_letter_id: string + dpag_shipment_id: string? + dpag_batch_id: string? + participant_id: string? + category: success | fail | undef + subclass: string + confidence: low | medium | high + strongest_signal: string? + evidence_summary: + - string + recommended_coordination_interpretation: string? +``` + +### 22.1 DPAG Adapter Success Subclasses + +```text id="mqc4vu" +success.pdf_uploaded +success.validation_passed +success.positioning_applied +success.cover_sheet_generated +success.scheduled_dispatch_accepted +success.submitted_for_sending +success.production_started +success.production_completed +success.handed_to_postal_service +success.registered_tracking_event +success.delivery_confirmed +success.return_info_processed +``` + +### 22.2 DPAG Adapter Fail Subclasses + +```text id="vopxjx" +fail.upload_rejected +fail.validation_failed +fail.invalid_pdf +fail.invalid_pdfa +fail.address_position_invalid +fail.automatic_positioning_failed +fail.cover_sheet_failed +fail.address_invalid +fail.unsupported_country +fail.unsupported_product +fail.scheduled_dispatch_invalid +fail.provider_rejected +fail.production_failed +fail.cancelled +fail.undeliverable +fail.return_received +``` + +### 22.3 DPAG Adapter Undef Subclasses + +```text id="1wfu6k" +undef.pending +undef.uploaded_only +undef.validation_pending +undef.action_required +undef.ready_but_not_sent +undef.scheduled_for_future_dispatch +undef.submitted_for_sending +undef.processing +undef.production_status_unknown +undef.production_completed_but_handover_unproven +undef.handed_to_postal_service_but_delivery_unproven +undef.tracking_pending +undef.registered_tracking_pending +undef.no_final_status_expected +undef.bulk_status_partial +undef.conflicting_evidence +undef.channel_degraded +``` + +## 23. DPAG Workflow States + +The DPAG flavor SHOULD support these normalized workflow states: + +```text id="6ob99h" +created +upload_requested +uploaded +upload_failed +validation_pending +validation_passed +validation_failed +action_required +positioning_pending +positioning_applied +positioning_failed +cover_sheet_pending +cover_sheet_generated +cover_sheet_failed +ready_for_sending +scheduled +send_requested +submitted_for_sending +processing +production_started +production_completed +handed_to_postal_service +in_transit +registered_event +delivery_confirmed +undeliverable +return_received +cancelled +failed +unknown +``` + +These are DPAG-flavor states, not coordination result states. + +## 24. DPAG Idempotency and Duplicate Send Prevention + +Duplicate physical send prevention is mandatory. + +If the DPAG endpoint used does not provide native idempotency, `hybridmail-connect` MUST implement adapter-managed idempotency. + +Idempotency scope SHOULD include: + +```text id="5xy9o4" +coordination_case_id +participant_id +delivery_id +payload_id +payload_integrity_hash +provider_flavor +postal recipient +shipment options +scheduled dispatch date +postal product +``` + +A repeated `delivery.submit_letter` request with the same idempotency key MUST NOT create a second physical shipment. + +If the repeated request refers to a changed payload, changed address, changed schedule, or changed product, the adapter MUST reject it as an idempotency conflict unless a new idempotency key is provided. + +## 25. DPAG Authentication and Access Handling + +The DPAG flavor SHOULD model authentication separately from business events. + +Public material indicates activation/API access and technical login/access handling are relevant. + +```yaml id="0b07q8" +DpagAuthContext: + api_account_ref: string + technical_login_ref: string? + token_ref: string? + authentication_mode: oauth2 | technical_login | api_key | provider_specific | unknown + token_expires_at: timestamp? + activation_state: not_activated | activated | suspended | unknown +``` + +Authentication failures SHOULD map to: + +```text id="3884ws" +system.provider.unavailable +notification/delivery action error with category: authentication +``` + +unless the provider returns a business-level rejection for the submitted shipment. + +## 26. DPAG Status Polling and Webhooks + +The DPAG flavor MUST support polling if the API exposes status retrieval. + +The DPAG flavor MAY support webhooks if available in the active provider setup. + +```yaml id="dyea5l" +DpagStatusPollingConfig: + enabled: boolean + initial_delay_seconds: integer + interval_seconds: integer + max_duration_seconds: integer + supports_bulk_status: boolean + supports_tracking_status: boolean + terminal_statuses: + - delivery_confirmed + - handed_to_postal_service + - undeliverable + - returned + - failed + - cancelled +``` + +Polling MUST preserve status history and MUST NOT overwrite raw evidence. + +## 27. DPAG Raw Event Preservation + +The DPAG flavor SHOULD preserve raw provider responses or references to them. + +```yaml id="b3p9qg" +RawDpagEventRef: + raw_event_id: string + source: api_response | status_poll | bulk_status_poll | tracking_poll | webhook | validation | production_data | operator + endpoint: string? + storage_ref: string? + received_at: timestamp + redacted: boolean +``` + +Normalized events SHOULD reference raw DPAG event data where available. + +## 28. DPAG Channel Health + +```yaml id="mcyrjl" +DpagChannelHealth: + provider_name: dpag_epost + provider_account_ref: string? + status: healthy | degraded | failing | unknown + activation_status: activated | not_activated | suspended | unknown + authentication_status: valid | expired | missing | insufficient | unknown + upload_status: healthy | degraded | unavailable | unknown + validation_status: healthy | degraded | unavailable | unknown + production_status: healthy | delayed | unavailable | unknown + status_api_status: healthy | degraded | unavailable | unknown + tracking_status: healthy | degraded | unavailable | product_dependent | unknown + bulk_status_api_status: healthy | degraded | unavailable | unknown + cutoff_status: before_cutoff | after_cutoff | no_production_day | unknown + known_degradations: + - string +``` + +Health events SHOULD map to: + +```text id="l2svm8" +system.provider.degraded +system.provider.unavailable +system.adapter.health_changed +``` + +## 29. Security Requirements + +The DPAG flavor MUST: + +* protect DPAG API credentials and technical login data. +* use secure transport. +* avoid logging document contents. +* protect postal recipient data. +* preserve idempotency. +* prevent duplicate physical shipments. +* separate test/sandbox and production configuration. +* protect tracking and status data. +* validate file type and size before upload where possible. +* protect generated cover sheets and derived print artifacts. +* apply tenant/account separation where applicable. + +## 30. Privacy Requirements + +The DPAG flavor SHOULD: + +* store payload references instead of payload content where possible. +* support metadata-only mode after provider submission. +* mask postal address data in logs. +* support configurable retention of raw provider responses. +* support configurable retention of generated cover sheets and artifacts. +* separate operational diagnostics from coordination evidence. +* document provider-side retention limitations. +* support deletion or anonymization workflows where legally possible. + +## 31. Reliability Requirements + +The DPAG flavor MUST support: + +* idempotent upload and send requests. +* duplicate status event detection. +* out-of-order status handling. +* polling-based status recovery. +* bulk status retrieval and partial-state handling. +* late tracking events. +* late return/undeliverable events. +* retryable upload failures. +* non-retryable validation failures. +* provider timeout handling. +* validation issue preservation. +* tracking history preservation. +* correlation preservation. +* dead-letter handling for unprocessable provider responses. + +## 32. Minimal API Surface + +The DPAG flavor SHOULD implement or expose these conceptual operations through `hybridmail-connect`. + +### 32.1 Adapter Contract Operations + +```text id="03530g" +GET /adapter/descriptor +GET /adapter/health +POST /adapter/actions +POST /adapter/events/provider +GET /adapter/events +GET /adapter/letters/{id}/timeline +GET /adapter/letters/{id}/assessment +``` + +### 32.2 DPAG Flavor Operations + +```text id="8csqtm" +POST /dpag/shipments +POST /dpag/shipments/bulk +POST /dpag/shipments/{id}/validate +POST /dpag/shipments/{id}/send +POST /dpag/shipments/{id}/cancel +GET /dpag/shipments/{id} +GET /dpag/shipments/{id}/status +GET /dpag/shipments/{id}/tracking +GET /dpag/shipments/bulk/{batch_id}/status +GET /dpag/shipments/{id}/timeline +GET /dpag/shipments/{id}/assessment +GET /dpag/channel-health +``` + +The exact provider endpoint names MUST be implemented according to the live DPAG/E-POSTBUSINESS API documentation. The conceptual operations above define the adapter-facing semantic model. + +## 33. Example End-to-End Flow + +### 33.1 Single DPAG Hybrid-Mail Shipment + +1. `coordination-engine` creates a coordination case. +2. A PDF or PDF/A payload is registered. +3. Policy selects `hybridmail-connect` with provider flavor `dpag_epost`. +4. `coordination-engine` sends `delivery.submit_letter`. +5. `hybridmail-connect` submits the PDF to DPAG. +6. DPAG accepts the submission. +7. The adapter emits `delivery.payload.accepted`. +8. DPAG validation/status indicates the shipment is processable. +9. The adapter emits `payload.validation_passed`. +10. If address-window positioning, automatic positioning, or cover-sheet generation is used, the adapter records the result as operational metadata. +11. The shipment is submitted or scheduled for sending. +12. The adapter emits `delivery.payload.submitted`. +13. Production data indicates processing/production. +14. The adapter emits `delivery.production.started` and later `delivery.production.completed` if semantically supported. +15. DPAG status indicates handover to the postal chain. +16. The adapter emits `delivery.postal.handed_over`. +17. `coordination-engine` evaluates whether postal handover is sufficient for the case policy. + +### 33.2 Validation Failure + +1. DPAG rejects or flags the PDF due to PDF/PDF-A, address-window, cover-sheet, product, or layout issue. +2. The adapter emits `payload.validation_failed`. +3. The validation issue is preserved with category and severity. +4. `coordination-engine` marks the participant delivery as action-required or failed for this attempt. +5. Policy requests document correction, alternate generation, or fallback channel. + +### 33.3 Scheduled Dispatch + +1. `coordination-engine` submits a shipment with a desired dispatch date. +2. DPAG accepts the schedule. +3. The adapter records `scheduled_dispatch`. +4. Participant remains `undef.scheduled_for_future_dispatch`. +5. When the dispatch window begins and DPAG status progresses, the adapter emits production and postal events. +6. `coordination-engine` derives updated participant state. + +### 33.4 Registered Letter + +1. A shipment is submitted with a registered-mail product. +2. DPAG accepts and processes the shipment. +3. Registered tracking events are received. +4. The adapter emits `delivery.postal.in_transit` for intermediate tracking events. +5. If a delivery-confirming status is received, the adapter emits `delivery.postal.delivery_confirmed`. +6. `coordination-engine` evaluates whether this satisfies the intended result. + +### 33.5 Bulk Shipment + +1. `coordination-engine` creates a batch delivery case. +2. `hybridmail-connect` submits a bulk upload to DPAG. +3. DPAG returns batch and per-shipment status data. +4. The adapter records `DpagBulkStatus` and `DpagChildShipmentStatus`. +5. The adapter emits per-child evidence events. +6. `coordination-engine` aggregates participant-level states and does not treat batch acceptance as universal success. + +## 34. DPAG MVP Scope + +The first DPAG flavor implementation should include: + +1. DPAG provider flavor descriptor. +2. Adapter descriptor integration. +3. DPAG credential/config handling. +4. Single PDF/PDF-A shipment submission. +5. Bulk shipment submission model, at least simulated or structurally supported. +6. Validation result mapping. +7. Address-window / positioning metadata handling. +8. Scheduled dispatch metadata handling. +9. Sending submission. +10. Status polling. +11. Bulk status polling where available. +12. Production status mapping. +13. Shipment tracking mapping. +14. Registered-mail status mapping where product-supported. +15. Evidence event generation. +16. Shipment timeline. +17. DPAG evidence assessment. +18. Adapter-managed idempotency unless provider endpoint support is verified. + +### MVP Required DPAG Events + +```text id="5kdrfo" +delivery.payload.accepted +delivery.payload.rejected +payload.validation_passed +payload.validation_failed +delivery.payload.available +delivery.payload.submitted +delivery.production.started +delivery.production.completed +delivery.postal.handed_over +delivery.postal.in_transit +delivery.postal.delivery_confirmed +delivery.postal.undeliverable +delivery.postal.return_received +delivery.postal.status_unknown +system.provider.degraded +system.provider.unavailable +``` + +Where a provider status is unavailable or semantically unclear, the adapter MUST emit the weakest safe event and preserve the raw status. + +## 35. DPAG MVP Acceptance Criteria + +The DPAG flavor MVP is acceptable when it can: + +1. Accept a coordination-compatible `delivery.submit_letter` request. +2. Upload or simulate upload of a PDF/PDF-A DPAG shipment. +3. Preserve correlation and idempotency. +4. Prevent duplicate physical shipments for duplicate idempotency keys. +5. Map upload acceptance to `delivery.payload.accepted`. +6. Map validation success to `payload.validation_passed`. +7. Map PDF/PDF-A, address-window, or layout validation failure to `payload.validation_failed`. +8. Map send submission to `delivery.payload.submitted`. +9. Map production status to `delivery.production.started` or `delivery.production.completed` only when supported by native status semantics. +10. Map postal handover only when semantically supported by provider status. +11. Map delivery confirmation only when the selected product/status supports it. +12. Map bulk child statuses per shipment where available. +13. Provide a DPAG shipment timeline. +14. Provide a DPAG evidence assessment. +15. Integrate with `coordination-engine` without overclaiming physical delivery or human awareness. + +## 36. Open Questions + +1. Which exact DPAG native statuses map to `delivery.production.started`, `delivery.production.completed`, `delivery.postal.handed_over`, and `delivery.postal.delivery_confirmed`? +2. Which endpoint names and payload fields are present in the active E-POSTBUSINESS API version used by the target account? +3. Does the target DPAG account/API configuration expose webhooks, or is polling the required baseline? +4. Which registered-mail and delivery-confirmation products are available in the target account? +5. Which PDF/A constraints are enforced automatically and which are documented as implementer responsibility? +6. Which address-window positioning modes are available in the active API? +7. How are individual cover sheets represented in the API? +8. How is scheduled dispatch represented and how are missed schedules surfaced? +9. Which bulk status fields provide child-shipment identity and per-recipient status? +10. Should DPAG validation issues update a shared postal address quality registry? +11. Does the active API provide native idempotency, or must idempotency remain fully adapter-managed? +12. Which GoGreen Plus or sustainability metadata should be captured for reporting? + +## 37. Non-Goals + +The DPAG flavor is not: + +* a document authoring system. +* a PDF renderer by default. +* a legal notice system by itself. +* a postal carrier abstraction for all DPAG products. +* a general E-POST UI replacement. +* the owner of coordination case success. +* the owner of contract, payment, or signature result semantics. + +It integrates DPAG/E-POSTBUSINESS hybrid-mail capabilities into the coordination framework. + +## 38. Summary + +`HybridmailDpagSpecification.md` defines the DPAG/E-POSTBUSINESS provider flavor for `hybridmail-connect`. + +The DPAG flavor models Deutsche Post hybrid-mail shipment as a PDF-centered physical-mail dispatch channel with: + +* PDF/PDF-A shipment submission +* one-by-one and bulk upload +* validation +* address-window positioning aids +* automatic positioning +* individual cover sheets +* scheduled dispatch +* production data +* shipment tracking +* registered-mail / delivery-confirmation products where supported +* bulk and per-shipment status +* postal handover and return/undeliverable evidence + +The key rule is: + +> DPAG/E-POSTBUSINESS events are provider, production, postal-chain, shipment-tracking, and registered-mail evidence. They are not automatic coordination result satisfaction. hybridmail-connect reports DPAG-channel facts and uncertainty. coordination-engine evaluates intended results. + diff --git a/spec/HybridmailPingenSpecification.md b/spec/HybridmailPingenSpecification.md new file mode 100644 index 0000000..01f4975 --- /dev/null +++ b/spec/HybridmailPingenSpecification.md @@ -0,0 +1,1165 @@ +# HybridmailPingenSpecification.md + +## 1. Document Status + +**Document:** HybridmailPingenSpecification.md +**Project:** hybridmail-connect +**Provider Flavor:** Pingen +**Parent Specification:** HybridmailAdapterSpecification.md +**Target Integration:** coordination-engine +**Adapter Contract:** AdapterInterfaceSpecification.md v1.0 +**Status:** Draft v1.0 +**Primary Scope:** Pingen-specific hybrid-mail provider flavor for letter upload, letter creation, validation, auto-send/manual-send control, Track & Trace, webhooks, return mail, delivered/undeliverable status mapping, and idempotent API usage. + +## 2. Purpose + +This document specifies the Pingen provider flavor for `hybridmail-connect`. + +The Pingen flavor implements the generic hybrid-mail adapter model for the Pingen Letter API. It maps Pingen’s API concepts into the provider-neutral hybrid-mail model used by `coordination-engine`. + +The purpose of this document is to capture: + +* Pingen-specific provider capabilities. +* Pingen-specific workflow stages. +* Pingen letter, file, address, validation, send, status, Track & Trace, and webhook concepts. +* Pingen return-mail and undeliverable processing. +* Pingen delivery-confirmation semantics. +* Pingen idempotency and rate-limit behavior. +* Pingen evidence event mapping. +* Pingen-specific limitations and assumptions. +* Pingen-specific MVP boundaries. + +The Pingen flavor MUST remain compatible with `HybridmailAdapterSpecification.md` and MUST NOT leak Pingen-specific terminology into the core coordination model except through mapped provider metadata. + +## 3. Grounding Summary + +The public Pingen material indicates the following relevant features: + +* Pingen provides a Letter API for automated postal-letter sending. +* Pingen offers SDKs, including PHP, Python, Go, .NET, and code examples. +* API authentication uses OAuth-style client credentials in common tooling/examples. +* Pingen supports Idempotency-Key headers. +* Pingen supports rate limiting and configurable send limits. +* Pingen offers staging/sandbox or full-feature simulation. +* Pingen supports document validation. +* Pingen supports configurable address position. +* Pingen supports auto-send control. +* Pingen offers Track & Trace for real-time progress transparency. +* Pingen provides webhooks for status changes. +* Public webhook categories include letter issues, sent letters, undeliverable letters, and delivered letters. +* Pingen automates return-mail processing and can provide return details via email and webhook. +* Pingen distinguishes ordinary postal dispatch from actual delivery-confirmation products. Ordinary letters may not receive final post-office delivery confirmation. + +These facts define the baseline for this provider flavor. + +## 4. Provider Flavor Identity + +```yaml +HybridmailProviderFlavorDescriptor: + provider_name: pingen + provider_label: Pingen Letter API + provider_family: hybridmail + adapter_contract_version: "1.0" + parent_specification: HybridmailAdapterSpecification.md + deployment_mode: external +``` + +## 5. Pingen Capability Descriptor + +The Pingen flavor MUST expose a capability descriptor. + +```yaml +HybridmailProviderFlavorDescriptor: + provider_name: pingen + provider_version: provider_specific + supported_file_types: + - pdf + supported_attachment_file_types: + - provider_specific + supports_single_letter: true + supports_bulk: true + supports_serial_letters: true + supports_attachments: provider_specific + supports_preview: true + supports_webhooks: true + supports_polling: true + supports_track_and_trace: true + supports_registered_mail: true + supports_delivery_confirmation: product_dependent + supports_return_mail: true + supports_address_correction: provider_specific + supports_international_mail: true + supports_cancellation: provider_specific + supports_staging: true + supports_full_feature_simulation: true + supports_idempotency: true + supports_rate_limits: true + supports_configurable_send_limits: true + supports_auto_send: true + supports_address_position_configuration: true + supports_document_validation: true + supported_print_options: + color_mode: provider_specific + simplex_duplex: provider_specific + envelope_selection: provider_specific + address_position: true + supported_postal_products: + - standard + - registered + - delivery_confirmation + - international + - provider_specific + limitations: + - Ordinary postal letters usually do not provide final post-office delivery confirmation. + - Delivered-letter webhook semantics must be interpreted according to selected product and Pingen status details. + - Return-mail information may arrive late. + - Public documentation confirms webhook categories, but exact event payload fields must be implemented against the live Pingen API documentation. + - Provider-native status names must be preserved in metadata and mapped conservatively. +``` + +## 6. Pingen-Specific Conceptual Workflow + +The Pingen flavor uses a file/letter-centric workflow. + +```text +authenticate +→ upload file / create letter +→ configure address position and sending options +→ validate letter +→ optionally auto-send or manually trigger send +→ Track & Trace status changes +→ webhook or polling status ingestion +→ sent-letter evidence +→ undeliverable/return-mail evidence where applicable +→ delivered-letter evidence where product/status supports it +``` + +The adapter MUST preserve this distinction: + +```text +Letter creation is not sending. +Validation success is not postal dispatch. +Sent status is not necessarily final delivery. +Ordinary postal dispatch is not proof of human awareness. +Return-mail events may arrive late and can change the delivery assessment. +``` + +## 7. Pingen Object Mapping + +## 7.1 Generic to Pingen Mapping + +| Generic hybrid-mail concept | Pingen flavor concept | +| --------------------------- | --------------------------------------------------------------------------------------------- | +| `HybridmailLetter` | Pingen letter | +| `HybridmailDocumentRef` | Uploaded file / letter document | +| `HybridmailAttempt` | Pingen letter creation/send operation | +| `HybridmailBatch` | Bulk or serial letter group where used | +| `PostalRecipient` | Address extracted from document or configured via address position / metadata where supported | +| `HybridmailOptions` | Pingen send, print, address, and delivery options | +| `HybridmailPreview` | Pingen preview or validation representation where supported | +| `HybridmailTimeline` | Pingen Track & Trace and webhook timeline | + +## 7.2 PingenLetter + +```yaml +PingenLetter: + pingen_letter_id: string + hybridmail_letter_id: string? + coordination_case_id: string? + participant_id: string? + provider_status: string? + normalized_status: string + file_ref: ResourceRef + page_count: integer? + sheet_count: integer? + address_position: PingenAddressPosition? + options: PingenLetterOptions? + validation_result: PingenValidationResult? + track_and_trace_ref: string? + webhook_event_refs: + - string + created_at: timestamp + updated_at: timestamp? + metadata: object? +``` + +## 7.3 PingenFile + +```yaml +PingenFile: + pingen_file_id: string? + hybridmail_letter_id: string? + original_file_ref: ResourceRef + file_type: pdf | unknown + file_size_bytes: integer? + integrity_hash: string? + upload_state: pending | uploaded | rejected | failed | unknown + created_at: timestamp + metadata: object? +``` + +## 7.4 PingenAttempt + +```yaml +PingenAttempt: + pingen_attempt_id: string + hybridmail_letter_id: string + pingen_letter_id: string? + provider_operation_id: string? + idempotency_key: string + operation_type: create_letter | validate_letter | send_letter | cancel_letter | status_poll | webhook_ingest + state: pending | accepted | rejected | failed | completed | duplicate | unknown + created_at: timestamp + updated_at: timestamp? + raw_provider_response_ref: string? +``` + +## 7.5 PingenBatch + +```yaml +PingenBatch: + pingen_batch_id: string? + hybridmail_batch_id: string + coordination_case_id: string? + batch_type: bulk | serial | campaign | mixed | unknown + letters: + - hybridmail_letter_id + state: pending | partial | completed | failed | unknown + created_at: timestamp + updated_at: timestamp? +``` + +Batch-level events MUST NOT be treated as success for every child letter unless Pingen provides per-letter status or policy explicitly accepts batch-level evidence. + +## 8. Pingen Address Model + +Pingen supports configurable address position. The Pingen flavor MUST model address positioning explicitly. + +```yaml +PingenAddressPosition: + source: configured | detected | provider_default | unknown + position: left | right | provider_specific | unknown + page_number: integer? + bounding_box: object? + confidence: low | medium | high +``` + +Generic postal recipient mapping: + +```yaml +PostalRecipient: + recipient_id: string? + name_lines: + - string + organization: string? + street: string? + house_number: string? + address_addition: string? + postal_code: string? + city: string? + region: string? + country_code: string? + country_name: string? + address_source: extracted_from_document | provided_metadata | provider_detected | manual | unknown + address_quality: PostalAddressQuality? +``` + +If Pingen validation detects address issues, they MUST be mapped into `PingenValidationIssue` and then into generic `HybridmailValidationIssue`. + +## 9. Pingen Validation Model + +Pingen validation results MUST be mapped into generic `HybridmailValidationResult`. + +```yaml +PingenValidationResult: + pingen_letter_id: string + provider_validation_state: string? + validation_state: pending | passed | action_required | failed | unknown + issues: + - PingenValidationIssue + validated_at: timestamp? + raw_provider_response_ref: string? +``` + +```yaml +PingenValidationIssue: + provider_issue_code: string? + issue_code: string + severity: info | warning | action_required | fatal + category: format | page_size | pdf | address | address_position | restricted_area | margins | file_size | page_count | postage | country | provider_policy | unknown + message: string + page_number: integer? + bounding_box: object? + fixable: boolean? +``` + +## 10. Pingen Validation Issue Mapping + +| Pingen validation concern | Generic validation category | Normalized issue code | +| --------------------------------- | --------------------------- | ------------------------------- | +| Invalid PDF | `pdf` / `format` | `invalid_pdf` | +| Unsupported file type | `format` | `unsupported_file_type` | +| Address not found or unreadable | `address` | `address_not_detected` | +| Address position invalid | `address_position` | `address_position_invalid` | +| Address format invalid | `address` | `address_format_invalid` | +| Unsupported destination country | `country` | `unsupported_country` | +| Page or file constraints violated | `page_count` / `file_size` | `document_constraint_violation` | +| Postal product invalid | `postage` | `unsupported_product` | +| Provider policy rejection | `provider_policy` | `provider_policy_rejected` | +| Unknown validation issue | `unknown` | `unknown_validation_issue` | + +Validation failure MUST emit: + +```text +payload.validation_failed +``` + +Validation success MUST emit: + +```text +payload.validation_passed +``` + +If Pingen classifies an issue as fixable/action-required, the adapter SHOULD include severity `action_required` rather than immediately treating the coordination case as permanently failed. + +## 11. Pingen Letter Options + +The Pingen flavor MUST map Pingen options into the generic `HybridmailOptions`. + +```yaml +PingenLetterOptions: + color_mode: color | grayscale | provider_default | unknown + print_sides: simplex | duplex | provider_default | unknown + address_position: left | right | provider_default | unknown + postage_product: standard | registered | delivery_confirmation | international | provider_specific + country_scope: domestic | international | unknown + auto_send: boolean? + dispatch_mode: manual_review | auto_send | submit_later | scheduled + desired_dispatch_date: date? + return_mail_handling: none | provider_processed | scan_return | digital_return_info | physical_return | unknown + delivery_confirmation_requested: boolean? + metadata: object? +``` + +Provider-native option names MUST be preserved in metadata. + +The adapter MUST NOT assume support for a postal product unless the Pingen capability descriptor confirms it. + +## 12. Pingen Auto-Send Handling + +Pingen supports auto-send behavior according to public API materials. + +The adapter MUST explicitly model auto-send, because it affects the transition from creation/validation to physical dispatch. + +```yaml +PingenAutoSendPolicy: + auto_send: boolean + send_after_validation: boolean? + require_adapter_approval: boolean? + require_coordination_policy_approval: boolean? +``` + +Recommended safety rule: + +```text +For high-assurance or expensive physical-mail scenarios, +auto_send SHOULD default to false unless explicitly enabled by policy. +``` + +If `auto_send=true`, the adapter MUST make duplicate-send prevention especially strict. + +## 13. Pingen Track & Trace Model + +Pingen provides Track & Trace progress updates. The adapter MUST represent those as a timeline. + +```yaml +PingenTrackAndTraceEvent: + event_id: string + pingen_letter_id: string + provider_status: string + normalized_status: string + event_category: letter_issue | sent_letter | undeliverable_letter | delivered_letter | return_mail | processing | unknown + occurred_at: timestamp? + observed_at: timestamp + raw_provider_response_ref: string? + metadata: object? +``` + +Track & Trace events should map to the strongest safe generic event. + +If the event only indicates process progress, it MUST NOT be mapped to postal delivery confirmation. + +## 14. Pingen Webhook Model + +Pingen public help identifies webhook categories: + +```text +Letter issues +Sent letters +Undeliverable letters +Delivered letters +``` + +The adapter MUST support these categories where available. + +```yaml +PingenWebhookEvent: + pingen_webhook_event_id: string + webhook_category: letter_issue | sent_letter | undeliverable_letter | delivered_letter | unknown + pingen_letter_id: string? + provider_event_id: string? + occurred_at: timestamp? + observed_at: timestamp + raw_payload_ref: string? + normalized_events: + - EvidenceEvent +``` + +## 15. Pingen Webhook Category Mapping + +| Pingen webhook category | Generic event | Evidence interpretation | +| ----------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------- | +| Letter issues | `payload.validation_failed` or `delivery.payload.rejected` | Strong issue/failure/action-required evidence depending payload | +| Sent letters | `delivery.postal.handed_over` or `delivery.payload.submitted` | Strong dispatch evidence only if semantics confirm physical send/handover | +| Undeliverable letters | `delivery.postal.undeliverable` or `delivery.postal.return_received` | Strong negative delivery evidence | +| Delivered letters | `delivery.postal.delivery_confirmed` | Strong delivery evidence if product/status supports delivery confirmation | + +Important: + +```text +A "sent letters" event should not be interpreted as "recipient received the letter". +A "delivered letters" event must be interpreted according to the postal product and provider semantics. +``` + +## 16. Pingen Return Mail Model + +Pingen supports automated return-mail processing and provides return details via channels including webhooks. + +```yaml +PingenReturnEvent: + return_event_id: string + pingen_letter_id: string + provider_return_id: string? + return_type: undeliverable | address_unknown | recipient_moved | refused | insufficient_address | other | unknown + return_details: string? + address_correction: PostalRecipient? + return_document_ref: ResourceRef? + occurred_at: timestamp? + observed_at: timestamp + raw_event_ref: string? +``` + +Return events usually create strong negative delivery evidence: + +```text +delivery.postal.return_received +delivery.postal.undeliverable +``` + +Return events may arrive late and MUST be preserved even if the coordination case has already progressed. + +## 17. Pingen Delivery Confirmation Model + +Pingen delivery-confirmation evidence is product-dependent. + +```yaml +PingenDeliveryConfirmation: + confirmation_id: string + pingen_letter_id: string + product_type: delivery_confirmation | registered | provider_specific + provider_status: string? + normalized_status: delivered | attempted | refused | undeliverable | returned | unknown + occurred_at: timestamp? + observed_at: timestamp + raw_event_ref: string? +``` + +The adapter MUST only emit: + +```text +delivery.postal.delivery_confirmed +``` + +when the Pingen status and selected postal product semantically support delivery confirmation. + +Ordinary letter dispatch MUST NOT be mapped to `delivery.postal.delivery_confirmed`. + +## 18. Pingen Status Model + +The Pingen flavor MUST preserve raw provider status and map it to normalized status. + +```yaml +PingenStatusRecord: + status_record_id: string + pingen_letter_id: string + provider_status: string + normalized_status: string + status_scope: letter | validation | sending | track_and_trace | webhook | return_mail | unknown + observed_at: timestamp + raw_provider_response_ref: string? +``` + +## 19. Normalized Pingen Status Categories + +The Pingen flavor SHOULD map native statuses into these normalized categories: + +```text +letter.created +letter.uploaded +letter.validation_pending +letter.validation_passed +letter.validation_failed +letter.action_required +letter.ready_for_sending +letter.submitted_for_sending +letter.processing +letter.handed_to_postal_service +letter.sent +letter.delivery_confirmed +letter.undeliverable +letter.return_received +letter.cancelled +letter.failed +letter.status_unknown +``` + +Where Pingen status granularity is insufficient, the adapter MUST use the nearest weaker event and preserve the raw status in metadata. + +## 20. Pingen-to-Generic Event Mapping + +| Pingen workflow event | Generic event | Evidence interpretation | +| -------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------- | +| File/letter accepted | `delivery.payload.accepted` | Digital submission accepted | +| Letter rejected | `delivery.payload.rejected` | Attempt failed | +| Validation passed | `payload.validation_passed` | Provider-processable | +| Letter issue | `payload.validation_failed` or `delivery.payload.rejected` | Issue/action-required/failure | +| Ready for sending | `delivery.payload.available` | Operational readiness | +| Auto-send triggered | `delivery.payload.submitted` | Provider instructed to send | +| Manual send triggered | `delivery.payload.submitted` | Provider instructed to send | +| Processing | `delivery.production.started` | Provider processing began | +| Sent-letter event | `delivery.postal.handed_over` if semantics support; otherwise `delivery.payload.submitted` | Strong dispatch or send-process evidence | +| Delivered-letter event | `delivery.postal.delivery_confirmed` if product/status supports | Strong delivery evidence | +| Undeliverable-letter event | `delivery.postal.undeliverable` | Strong negative evidence | +| Return details received | `delivery.postal.return_received` | Strong negative evidence | +| Track & Trace update | status-specific mapped event | Preserve raw status | +| Cancellation | `delivery.payload.failed` or cancellation metadata | Attempt stopped | + +## 21. Evidence Grading Rules + +### 21.1 Pingen Letter Accepted + +```yaml +event_type: delivery.payload.accepted +evidence_grade: + strength: weak + actor_certainty: none + authority_certainty: none + payload_certainty: medium + interaction_certainty: none + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: low +notes: + - Pingen accepted the letter or file into the API workflow. + - This does not prove validation, sending, postal handover, or delivery. +``` + +### 21.2 Pingen Validation Passed + +```yaml +event_type: payload.validation_passed +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Pingen validation passed. + - The letter can proceed to sending according to provider rules. +``` + +### 21.3 Pingen Letter Issue / Validation Failed + +```yaml +event_type: payload.validation_failed +evidence_grade: + strength: negative + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Pingen reported a letter issue or validation failure. + - The issue should be categorized and preserved. +``` + +### 21.4 Pingen Sent-Letter Event + +```yaml +event_type: delivery.postal.handed_over +evidence_grade: + strength: strong + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - Pingen reports the letter as sent or handed into the postal process. + - This is strong dispatch evidence. + - It does not prove that the recipient received or read the letter. +``` + +If the native status only means “submitted for sending” and not postal handover, the adapter MUST instead map to: + +```text +delivery.payload.submitted +``` + +### 21.5 Pingen Delivered-Letter Event + +```yaml +event_type: delivery.postal.delivery_confirmed +evidence_grade: + strength: strong + actor_certainty: low + authority_certainty: none + payload_certainty: high + interaction_certainty: low + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - Pingen reports delivered-letter evidence. + - This event is product- and status-dependent. + - It may still not prove that the intended human personally read the payload. +``` + +### 21.6 Pingen Undeliverable / Return Mail + +```yaml +event_type: delivery.postal.return_received +evidence_grade: + strength: negative + actor_certainty: none + authority_certainty: none + payload_certainty: high + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - Pingen reports undeliverable or return-mail processing. + - This is strong negative evidence for the physical delivery attempt. +``` + +## 22. Pingen Evidence Assessment + +The Pingen flavor SHOULD provide a Pingen-native evidence assessment. + +```yaml +PingenEvidenceAssessment: + hybridmail_letter_id: string + pingen_letter_id: string? + participant_id: string? + category: success | fail | undef + subclass: string + confidence: low | medium | high + strongest_signal: string? + evidence_summary: + - string + recommended_coordination_interpretation: string? +``` + +### 22.1 Pingen Adapter Success Subclasses + +```text +success.letter_created +success.validation_passed +success.ready_for_sending +success.submitted_for_sending +success.sent_letter +success.handed_to_postal_service +success.delivery_confirmed +success.return_info_processed +``` + +### 22.2 Pingen Adapter Fail Subclasses + +```text +fail.upload_rejected +fail.validation_failed +fail.letter_issue +fail.address_position_invalid +fail.address_invalid +fail.unsupported_file_type +fail.unsupported_country +fail.product_invalid +fail.send_limit_exceeded +fail.provider_rejected +fail.cancelled +fail.undeliverable +fail.return_received +``` + +### 22.3 Pingen Adapter Undef Subclasses + +```text +undef.pending +undef.uploaded_only +undef.validation_pending +undef.action_required +undef.ready_but_not_sent +undef.auto_send_pending +undef.submitted_for_sending +undef.processing +undef.sent_but_delivery_unproven +undef.handed_to_postal_service_but_delivery_unproven +undef.track_and_trace_pending +undef.return_mail_pending +undef.no_final_status_expected +undef.delivered_status_semantics_unclear +undef.conflicting_evidence +undef.channel_degraded +``` + +## 23. Pingen Workflow States + +The Pingen flavor SHOULD support these normalized workflow states: + +```text +created +upload_requested +uploaded +upload_failed +validation_pending +validation_passed +validation_failed +action_required +ready_for_sending +auto_send_pending +send_requested +submitted_for_sending +processing +sent +handed_to_postal_service +delivery_confirmed +undeliverable +return_received +cancelled +failed +unknown +``` + +These are Pingen-flavor states, not coordination result states. + +## 24. Pingen Idempotency and Duplicate Send Prevention + +Pingen publicly supports Idempotency-Key headers. The Pingen flavor MUST use them for externally visible create/send operations where supported. + +The adapter MUST also maintain local idempotency records to prevent duplicate physical sends. + +Idempotency scope SHOULD include: + +```text +coordination_case_id +participant_id +delivery_id +payload_id +payload_integrity_hash +provider_flavor +postal recipient +letter options +auto_send flag +``` + +A repeated `delivery.submit_letter` request with the same idempotency key MUST NOT create a second physical send. + +If the repeated request refers to a changed payload, address, auto-send setting, or delivery product, the adapter MUST reject it as an idempotency conflict unless a new idempotency key is provided. + +## 25. Pingen Rate Limits and Send Limits + +Pingen publicly advertises rate limiting and configurable send limits. + +The adapter SHOULD model rate-limit and send-limit events. + +```yaml +PingenLimitState: + provider_account_ref: string? + rate_limit_status: available | limited | exhausted | unknown + send_limit_status: available | limited | exhausted | unknown + reset_at: timestamp? + metadata: object? +``` + +Relevant events: + +```text +system.provider.degraded +delivery.payload.rejected +``` + +If a send limit blocks dispatch, the adapter SHOULD classify the letter as: + +```text +undef.pending +``` + +if retry/continuation is expected, or: + +```text +fail.provider_rejected +``` + +if the provider rejects the request as final. + +## 26. Pingen Webhook Handling + +The Pingen flavor SHOULD prefer webhooks for status changes where available and use polling as a fallback. + +Webhook handling MUST: + +* verify authenticity where supported. +* deduplicate events. +* preserve raw payload references. +* map categories conservatively. +* accept late return-mail events. +* emit per-letter evidence events. +* avoid treating webhook delivery to the adapter as postal delivery to the recipient. + +```yaml +PingenWebhookHandling: + enabled: boolean + verification_mode: signature | shared_secret | ip_allowlist | none | unknown + subscribed_categories: + - letter_issue + - sent_letter + - undeliverable_letter + - delivered_letter + fallback_polling_enabled: boolean +``` + +## 27. Pingen Status Polling + +The Pingen flavor MUST support polling if the API exposes status retrieval. + +```yaml +PingenStatusPollingConfig: + enabled: boolean + initial_delay_seconds: integer + interval_seconds: integer + max_duration_seconds: integer + terminal_statuses: + - delivered + - undeliverable + - returned + - failed + - cancelled + no_final_status_expected_after_seconds: integer? +``` + +Polling MUST preserve status history and MUST NOT overwrite raw evidence. + +## 28. Pingen Raw Event Preservation + +The Pingen flavor SHOULD preserve raw provider responses or references to them. + +```yaml +RawPingenEventRef: + raw_event_id: string + source: api_response | status_poll | webhook | track_and_trace | validation | operator + endpoint: string? + storage_ref: string? + received_at: timestamp + redacted: boolean +``` + +Normalized events SHOULD reference raw Pingen event data where available. + +## 29. Pingen Channel Health + +```yaml +PingenChannelHealth: + provider_name: pingen + provider_account_ref: string? + status: healthy | degraded | failing | unknown + authentication_status: valid | expired | missing | insufficient | unknown + api_status: healthy | degraded | unavailable | unknown + upload_status: healthy | degraded | unavailable | unknown + validation_status: healthy | degraded | unavailable | unknown + track_and_trace_status: healthy | degraded | unavailable | unknown + webhook_status: healthy | degraded | unavailable | not_configured | unknown + rate_limit_status: available | limited | exhausted | unknown + send_limit_status: available | limited | exhausted | unknown + staging_status: healthy | degraded | unavailable | not_configured | unknown + known_degradations: + - string +``` + +Health events SHOULD map to: + +```text +system.provider.degraded +system.provider.unavailable +system.adapter.health_changed +``` + +## 30. Security Requirements + +The Pingen flavor MUST: + +* protect Pingen API credentials. +* use secure transport. +* use provider Idempotency-Key support where available. +* maintain local idempotency records. +* avoid duplicate physical sends. +* verify webhooks where supported. +* avoid logging document contents. +* protect postal recipient data. +* protect return-mail details. +* separate staging/sandbox and production configuration. +* support tenant/account separation where applicable. + +## 31. Privacy Requirements + +The Pingen flavor SHOULD: + +* store payload references instead of payload content where possible. +* support metadata-only mode after provider submission. +* mask postal address data in logs. +* support configurable retention of raw provider responses. +* support configurable retention of return-mail details. +* separate operational diagnostics from coordination evidence. +* document provider-side retention limitations. +* support deletion or anonymization workflows where legally possible. + +## 32. Reliability Requirements + +The Pingen flavor MUST support: + +* idempotent create/send requests. +* duplicate webhook event detection. +* out-of-order status handling. +* polling-based status recovery. +* late return-mail events. +* late delivered-letter events. +* retryable upload failures. +* non-retryable validation failures. +* provider timeout handling. +* validation issue preservation. +* rate-limit handling. +* send-limit handling. +* Track & Trace history preservation. +* correlation preservation. +* dead-letter handling for unprocessable provider responses. + +## 33. Minimal API Surface + +The Pingen flavor SHOULD implement or expose these conceptual operations through `hybridmail-connect`. + +### 33.1 Adapter Contract Operations + +```text +GET /adapter/descriptor +GET /adapter/health +POST /adapter/actions +POST /adapter/events/provider +GET /adapter/events +GET /adapter/letters/{id}/timeline +GET /adapter/letters/{id}/assessment +``` + +### 33.2 Pingen Flavor Operations + +```text +POST /pingen/letters +POST /pingen/letters/{id}/send +POST /pingen/letters/{id}/cancel +GET /pingen/letters/{id} +GET /pingen/letters/{id}/status +GET /pingen/letters/{id}/track-and-trace +GET /pingen/letters/{id}/return-info +GET /pingen/letters/{id}/delivery-confirmation +GET /pingen/letters/{id}/timeline +GET /pingen/letters/{id}/assessment +POST /pingen/webhooks +GET /pingen/channel-health +``` + +The exact provider endpoint names MUST be implemented according to the live Pingen API documentation. The conceptual operations above define the adapter-facing semantic model. + +## 34. Example End-to-End Flow + +### 34.1 Single Pingen Letter with Manual Send + +1. `coordination-engine` creates a coordination case. +2. A PDF payload is registered. +3. Policy selects `hybridmail-connect` with provider flavor `pingen`. +4. `coordination-engine` sends `delivery.submit_letter`. +5. `hybridmail-connect` creates a Pingen letter with `auto_send=false`. +6. Pingen accepts the letter. +7. The adapter emits `delivery.payload.accepted`. +8. Pingen validates the letter. +9. If validation passes, the adapter emits `payload.validation_passed`. +10. Policy approves sending. +11. The adapter sends or submits the letter. +12. The adapter emits `delivery.payload.submitted`. +13. Pingen Track & Trace reports sent-letter progress. +14. The adapter emits `delivery.postal.handed_over` only if the native event semantics support physical dispatch/handover. +15. `coordination-engine` evaluates whether dispatch evidence is sufficient for the case policy. + +### 34.2 Auto-Send Letter + +1. `coordination-engine` submits a letter with `auto_send=true`. +2. The adapter validates idempotency and payload identity. +3. Pingen accepts the request. +4. Validation and send may proceed without separate manual approval. +5. The adapter emits distinct events for validation and send stages as they become observable. +6. If validation fails, the adapter emits `payload.validation_failed`. +7. If sending proceeds, the adapter emits `delivery.payload.submitted` and later stronger status events. + +### 34.3 Pingen Letter Issue + +1. Pingen reports a letter issue through webhook or status polling. +2. The adapter classifies the issue. +3. The adapter emits `payload.validation_failed` or `delivery.payload.rejected`. +4. `coordination-engine` marks the participant attempt as action-required or failed. +5. Policy requests correction, alternate document generation, or fallback. + +### 34.4 Undeliverable / Return Mail + +1. A letter was sent. +2. Pingen later reports it as undeliverable or return mail is processed. +3. The adapter emits `delivery.postal.undeliverable` or `delivery.postal.return_received`. +4. `coordination-engine` updates the participant delivery assessment. +5. Policy may trigger address correction, alternate channel, or manual review. + +### 34.5 Delivered-Letter Product + +1. A letter is sent using a delivery-confirmation-capable product. +2. Pingen reports delivered-letter evidence. +3. The adapter confirms the product/status semantics. +4. The adapter emits `delivery.postal.delivery_confirmed`. +5. `coordination-engine` evaluates whether this satisfies the intended result. + +## 35. Pingen MVP Scope + +The first Pingen flavor implementation should include: + +1. Pingen provider flavor descriptor. +2. Adapter descriptor integration. +3. Pingen credential/config handling. +4. Letter creation or upload. +5. Auto-send flag support. +6. Address-position option mapping. +7. Validation result mapping. +8. Sending submission. +9. Track & Trace status polling. +10. Webhook ingestion for supported categories. +11. Return-mail / undeliverable event mapping. +12. Delivered-letter event mapping, product-dependent. +13. Evidence event generation. +14. Letter timeline. +15. Pingen evidence assessment. +16. Provider idempotency header usage. +17. Local idempotency protection. + +### MVP Required Pingen Events + +```text +delivery.payload.accepted +delivery.payload.rejected +payload.validation_passed +payload.validation_failed +delivery.payload.available +delivery.payload.submitted +delivery.production.started +delivery.postal.handed_over +delivery.postal.delivery_confirmed +delivery.postal.undeliverable +delivery.postal.return_received +delivery.postal.status_unknown +system.provider.degraded +system.provider.unavailable +``` + +Where a provider status is unavailable or semantically unclear, the adapter MUST emit the weakest safe event and preserve the raw status. + +## 36. Pingen MVP Acceptance Criteria + +The Pingen flavor MVP is acceptable when it can: + +1. Accept a coordination-compatible `delivery.submit_letter` request. +2. Create or simulate a Pingen letter. +3. Preserve correlation and idempotency. +4. Use Pingen Idempotency-Key support where available. +5. Prevent duplicate physical sends for duplicate idempotency keys. +6. Support auto-send configuration. +7. Map validation success to `payload.validation_passed`. +8. Map letter issues or validation failure to `payload.validation_failed`. +9. Map send submission to `delivery.payload.submitted`. +10. Map sent-letter evidence conservatively. +11. Map delivered-letter evidence only when provider/product semantics support delivery confirmation. +12. Map undeliverable/return-mail evidence to strong negative delivery evidence. +13. Provide a Pingen letter timeline. +14. Provide a Pingen evidence assessment. +15. Integrate with `coordination-engine` without overclaiming physical delivery or human awareness. + +## 37. Open Questions + +1. Which exact Pingen native statuses map to `delivery.production.started`, `delivery.postal.handed_over`, and `delivery.postal.delivery_confirmed`? +2. Which event payload fields are present for Pingen webhook categories in the target API version? +3. How does the target Pingen account expose delivery-confirmation products? +4. Which return-mail details are available through API versus email/webhook? +5. Does the implementation need organization/workspace selection before letter creation? +6. Which Pingen country/product combinations are enabled for the target account? +7. How should configurable send limits be surfaced to coordination-engine policy? +8. How should staging/full-feature simulation be represented in evidence grades? +9. Which address-position values are available in the live API? +10. Should Pingen return details update a shared postal address quality registry? + +## 38. Non-Goals + +The Pingen flavor is not: + +* a document authoring system. +* a PDF renderer by default. +* a legal notice system by itself. +* a postal carrier. +* a general Pingen UI replacement. +* the owner of coordination case success. +* the owner of contract, payment, or signature result semantics. + +It integrates Pingen hybrid-mail capabilities into the coordination framework. + +## 39. Summary + +`HybridmailPingenSpecification.md` defines the Pingen provider flavor for `hybridmail-connect`. + +The Pingen flavor models Pingen as a letter-centric hybrid-mail provider with: + +* letter/file creation +* validation +* configurable address position +* auto-send/manual-send control +* Track & Trace +* webhook status categories +* sent-letter evidence +* delivered-letter evidence where product-supported +* undeliverable and return-mail evidence +* idempotency and rate-limit support +* staging/simulation support + +The key rule is: + +> Pingen events are provider, production, postal-chain, Track & Trace, and return-mail evidence. They are not automatic coordination result satisfaction. hybridmail-connect reports Pingen-channel facts and uncertainty. coordination-engine evaluates intended results. + diff --git a/spec/ProductRequirementsDocument.md b/spec/ProductRequirementsDocument.md new file mode 100644 index 0000000..b04c0b6 --- /dev/null +++ b/spec/ProductRequirementsDocument.md @@ -0,0 +1,987 @@ +# Product Requirements Document: coordination-engine + +## 1. Product Name + +**coordination-engine** + +## 2. Product Intent + +**coordination-engine** is a generalized framework for digital coordination as goal- and result-driven communication between multiple interacting parties. It enables the easy generation of scenario-specific communication applications by selecting only the relevant concepts from the framework to establish well-defined, headless communication applications. + +The product implements the broader **coordination-framework** through a runtime **coordination-controller**, supporting reusable controllers for results, participants, payloads, access, notifications, deliveries, interactions, evidence, policy, and adapters. + +## 3. Executive Summary + +Digital communication should not be modeled primarily as the transport of messages from sender to receiver. In many modern scenarios, the more efficient and powerful model is to control access to payloads, action surfaces, and interaction flows while collecting evidence that the relevant parties became aware, accessed, acted, accepted, signed, paid, responded, or otherwise changed state. + +**coordination-engine** provides a headless coordination runtime for building such systems. It treats communication as a goal-directed coordination process involving participants, payloads, notifications, deliveries, access rights, interaction events, policies, deadlines, and intended results. + +Instead of asking only whether a message was sent or delivered, coordination-engine asks: + +* What result is the initiator trying to achieve? +* Which participants are involved? +* Which payloads, notifications, deliveries, and interactions are required? +* What evidence is sufficient to mark progress or completion? +* Which follow-up actions are required under uncertainty? +* When is the coordination case successful, failed, partial, expired, or escalated? + +The engine is intended to support many concrete applications, including digital postal delivery, document distribution, data collection, AGB acceptance, contract signing, invoice/payment collection, incident acknowledgement, approval workflows, onboarding, and regulated communication scenarios. + +## 4. Problem Statement + +Many digital communication systems are built around channels: email, SMS, push notifications, portals, messaging systems, file transfer, signature tools, payment systems, or CRM workflows. This leads to fragmented logic, duplicated business rules, weak evidence handling, and poor traceability. + +Traditional message-delivery thinking is insufficient for digital coordination because: + +1. **Sending is not success.** A message may be technically sent but never seen, understood, acted upon, or sufficient for the intended business result. +2. **Delivery is often access-based.** Digital payloads are often not transported to recipients but made available through controlled access. +3. **Multiple parties may interact.** Coordination can involve initiators, recipients, delegates, signers, payers, approvers, agents, systems, and intermediaries. +4. **Evidence matters.** Business outcomes require evidence of awareness, access, identity, authority, interaction, acceptance, payment, signature, or completion. +5. **Uncertainty is normal.** Many events produce only weak or ambiguous evidence, especially across channels like email. +6. **Follow-up must be policy-driven.** Reminders, escalation, fallback channels, revocation, retries, deadline handling, and manual review should be derived from explicit policies. +7. **Scenarios differ, but patterns repeat.** Digital postal delivery, contract signing, payment collection, and document collection use different adapters but similar coordination logic. + +coordination-engine addresses this by providing a reusable framework for result-oriented digital coordination. + +## 5. Product Vision + +coordination-engine should become a headless coordination runtime that allows developers, operators, and product teams to define and run scenario-specific coordination applications by composing reusable concepts instead of hardcoding channel-specific workflows. + +The long-term vision is: + +> Define the intended result, participants, payloads, required evidence, policies, and adapters — and let the coordination engine orchestrate the process until the result is achieved, failed, expired, escalated, or manually closed. + +## 6. Core Product Principle + +**A coordination case is successful when its intended result is satisfied, not when messages have merely been sent.** + +Digital coordination is the controlled arrangement of participants, payloads, access, notifications, deliveries, interactions, and evidence so that intended results can be achieved under uncertainty. + +## 7. Target Users + +### 7.1 Primary Users + +#### Application Developers + +Developers who need to build scenario-specific communication and coordination applications without reinventing delivery tracking, notification logic, evidence handling, and follow-up workflows. + +#### Product Teams + +Teams designing applications for document delivery, onboarding, contract closure, acceptance workflows, customer communication, payment collection, or compliance communication. + +#### Operations Teams + +Teams responsible for monitoring ongoing coordination cases, identifying unresolved participants, triggering manual follow-up, and reviewing evidence. + +#### Automation Agents + +Software agents that create, inspect, update, or close coordination cases through APIs or event streams. + +### 7.2 Secondary Users + +#### Compliance and Legal Stakeholders + +Stakeholders who need auditable evidence of notification, delivery, acceptance, signature, payment, or response. + +#### Business Analysts + +Analysts who define coordination scenarios, result predicates, thresholds, deadlines, and follow-up policies. + +#### Integration Partners + +Teams integrating email, SMS, push, payment, signature, identity, storage, CRM, ERP, portal, or messaging systems. + +## 8. Key Use Cases + +### 8.1 Digital Postal Delivery + +An initiator makes a PDF letter available to recipients through a portal. The system notifies recipients through email or other channels, observes access, records download/view evidence, and escalates unresolved cases. + +### 8.2 Document Distribution + +A company distributes a policy update, security notice, product change notice, or customer document to many recipients and wants to maximize access or acknowledgement within a defined timeframe. + +### 8.3 Data and Document Collection + +An initiator requests documents, forms, confirmations, or structured data from participants. The engine tracks notifications, access to the request, submissions, validation results, correction loops, and completion. + +### 8.4 AGB / Terms Acceptance + +A company publishes new terms and requires affected users or customers to review and accept them before a deadline. The engine tracks awareness, access, versioned payloads, authenticated acceptance, decline, and enforcement state. + +### 8.5 Contract Signing + +The system coordinates contract delivery, signer notification, access control, identity verification, signing, counter-signing, decline, expiry, and archival of final evidence. + +### 8.6 Payment Collection + +The system coordinates invoice availability, payment requests, notifications, payment links, payment attempts, successful settlement, failed payments, reminders, disputes, dunning escalation, and closure. + +### 8.7 Incident Acknowledgement + +The system sends urgent alerts, requires acknowledgements from relevant parties, escalates through multiple channels, and records time-to-awareness and response evidence. + +### 8.8 Approval Workflow + +The system coordinates one or more approvers around a payload, tracks access, review, approval, rejection, delegation, timeout, and escalation. + +## 9. Scope + +### 9.1 In Scope + +coordination-engine shall provide: + +* A generic model for coordination cases. +* A runtime coordination-controller. +* Participants, roles, endpoints, and participant states. +* Intended result definitions. +* Result evaluation and progress assessment. +* Payload/resource modeling. +* Access grants and access state tracking. +* Notification attempts and notification evidence. +* Delivery cases and delivery evidence. +* Interaction events and interaction state tracking. +* Evidence ledger with normalized events. +* Policy engine for follow-up decisions. +* Adapter interface for external systems. +* Scenario templates or patterns. +* Headless API for creating and managing coordination applications. +* Event-driven integration model. +* Audit trail for decisions and evidence. +* MVP support for at least one concrete scenario. + +### 9.2 Out of Scope for MVP + +The MVP does not need to provide: + +* A full user interface. +* A complete no-code workflow builder. +* Built-in support for every communication channel. +* Legal validity guarantees for signatures, notices, or contracts. +* Full BPMN compatibility. +* Full CRM, ERP, payment, or document management implementation. +* Native long-term archive implementation. +* AI-based policy optimization. +* Complex multi-party negotiation logic. + +These may be added later through adapters, scenario modules, or higher-level applications. + +## 10. Conceptual Model + +### 10.1 CoordinationCase + +A **CoordinationCase** is the main runtime object. It represents a goal-directed coordination process initiated by one party to achieve an intended result involving one or more participants. + +Core fields: + +* `id` +* `initiator` +* `purpose` +* `scenario_type` +* `intended_result` +* `participants` +* `payloads` +* `action_surfaces` +* `access_grants` +* `notifications` +* `deliveries` +* `interactions` +* `evidence_events` +* `policies` +* `deadlines` +* `assessment` +* `next_actions` +* `closure_state` + +### 10.2 Participant + +A **Participant** is an actor involved in the coordination case. + +Participants may be: + +* Initiators +* Recipients +* Respondents +* Signers +* Payers +* Approvers +* Delegates +* Agents +* Organizations +* Systems +* Intermediaries + +Each participant may have: + +* Identity references +* Contact endpoints +* Roles +* Authority attributes +* Required outcomes +* Participant-specific state +* Participant-specific evidence level +* Next actions + +### 10.3 IntendedResult + +An **IntendedResult** defines what success means. + +Examples: + +* Payload accessed by all required recipients. +* Valid submissions received from all required respondents. +* Terms accepted by all active users. +* Contract signed by all required parties. +* Invoices paid above a defined amount threshold. +* Incident acknowledged by all critical participants. + +An intended result should define: + +* Result type +* Target population +* Required outcome +* Required evidence level +* Quantitative threshold +* Deadline +* Partial success rules +* Failure rules +* Fallback policy + +### 10.4 Payload + +A **Payload** is a meaningful resource involved in the coordination case. + +Examples: + +* PDF letter +* Invoice +* Contract +* AGB version +* Form template +* Submitted document +* Payment request +* Signature envelope +* Receipt +* Structured data package + +Payloads may have: + +* Meaning +* Version +* Representation +* Instance +* Integrity hash +* Sensitivity +* Retention policy +* Access policy +* Validation rules + +### 10.5 ActionSurface + +An **ActionSurface** is the digital or physical place where a participant can interact with a payload or perform a required action. + +Examples: + +* Portal page +* Mobile app screen +* Payment page +* Signature flow +* Upload form +* Approval screen +* Chatbot flow +* API endpoint +* Email reply parser +* XMPP bot +* Social media direct message flow + +### 10.6 Notification + +A **Notification** is an awareness-oriented signal. + +Its purpose is to create awareness or prompt attention. Notification is not delivery of the primary payload, although it may contain a small inline payload. + +Examples: + +* Email notification +* SMS reminder +* Push message +* XMPP message +* RSS item +* In-app notification +* Social media direct message +* Webhook event + +### 10.7 Delivery + +A **Delivery** is the controlled availability, access, transfer, retrieval, submission, or consumption of a payload. + +Delivery may be: + +* Outbound: initiator to participant +* Inbound: participant to initiator +* Peer-to-peer: participant to participant +* System-to-system + +Examples: + +* PDF downloaded +* Contract viewed +* Form submitted +* Data package uploaded +* Payment confirmation received +* Signed document archived +* API payload consumed + +### 10.8 Interaction + +An **Interaction** is a meaningful action performed by a participant, system, agent, or intermediary on an action surface or payload. + +Examples: + +* Page loaded +* Notification opened +* Link clicked +* Portal login started +* Authentication completed +* Document viewed +* PDF downloaded +* Form submitted +* Signature completed +* Payment initiated +* Payment settled +* Decline submitted +* Dispute opened +* Acknowledgement recorded + +### 10.9 EvidenceEvent + +An **EvidenceEvent** is a normalized observation recorded by the engine or an adapter. + +Evidence events should record: + +* Event type +* Timestamp +* Source adapter +* Participant reference +* Actor reference +* Payload reference +* Action surface reference +* Raw event reference +* Normalized meaning +* Confidence +* Evidence grade +* Correlation ID + +### 10.10 Policy + +A **Policy** defines how the engine should interpret evidence and decide follow-up actions. + +Policies may define: + +* Required evidence levels +* Deadlines +* Reminder intervals +* Channel escalation rules +* Retry rules +* Fallback rules +* Manual review triggers +* Closure rules +* Failure rules +* Partial success rules +* Risk thresholds + +## 11. Controller Architecture + +### 11.1 Coordination Controller + +The coordination-controller is the main runtime component. + +Responsibilities: + +* Create and manage CoordinationCases. +* Track participant-level and case-level progress. +* Coordinate subordinate controllers. +* Maintain current assessment. +* Trigger policy evaluation. +* Request follow-up actions. +* Close, fail, expire, or escalate cases. + +### 11.2 Result Controller + +Responsibilities: + +* Evaluate whether the intended result has been satisfied. +* Track thresholds and deadlines. +* Determine participant-level completion. +* Determine case-level completion. +* Support partial, failed, expired, and manually overridden states. + +### 11.3 Participant Controller + +Responsibilities: + +* Manage participants and roles. +* Maintain participant-specific state. +* Link participants to endpoints, payloads, actions, and evidence. +* Support delegates, organizations, systems, and agents. + +### 11.4 Payload Controller + +Responsibilities: + +* Manage payload identity, versioning, representations, and integrity. +* Link payloads to intended results and participants. +* Support inbound and outbound payloads. +* Track payload availability, validation, and archival state. + +### 11.5 Access Controller + +Responsibilities: + +* Manage access grants. +* Control access to payloads and action surfaces. +* Track access attempts, use, expiry, denial, delegation, and revocation. +* Integrate with identity and authorization systems. + +### 11.6 Notification Controller + +Responsibilities: + +* Create awareness through one or more notification channels. +* Track notification attempts. +* Normalize notification evidence. +* Support reminders, channel switching, and escalation. +* Avoid treating weak technical delivery as strong awareness evidence. + +### 11.7 Delivery Controller + +Responsibilities: + +* Track payload delivery, retrieval, submission, or consumption. +* Record delivery-side evidence. +* Distinguish payload availability from payload access and payload completion. +* Feed delivery evidence into result and notification assessment. + +### 11.8 Interaction Controller + +Responsibilities: + +* Normalize meaningful participant interactions. +* Distinguish weak interaction, identity-bound interaction, and result-relevant interaction. +* Classify bot, proxy, scanner, anonymous, authenticated, delegated, and authorized interactions where possible. + +### 11.9 Identity & Authority Controller + +Responsibilities: + +* Evaluate actor identity. +* Evaluate authority and delegation. +* Link interactions to valid participants. +* Provide evidence grades for identity and authority. +* Integrate with IAM, SSO, MFA, signature, and authorization systems. + +### 11.10 Evidence Ledger + +Responsibilities: + +* Store normalized evidence events. +* Preserve raw event references. +* Support auditability and traceability. +* Support derived assessments. +* Allow reconstruction of why a case was marked successful, failed, partial, expired, or escalated. + +### 11.11 Policy Engine + +Responsibilities: + +* Evaluate policies against current evidence and state. +* Determine next actions. +* Trigger notifications, reminders, retries, escalations, fallback channels, manual reviews, or closure. +* Support scenario-specific policies. + +### 11.12 Adapter Layer + +Responsibilities: + +* Provide integration with external protocols, systems, and providers. +* Translate native events into normalized EvidenceEvents. +* Expose actions that can be invoked by the engine. +* Declare adapter capabilities, failure modes, evidence strength, identity strength, latency, and cost profile. + +Example adapters: + +* Email +* SMS +* Mobile push +* In-app notification +* Portal +* RSS/feed +* XMPP/chat +* Webhook/API +* Payment provider +* Signature provider +* Document storage +* Identity provider +* CRM +* ERP +* Archive system + +## 12. Scenario Patterns + +coordination-engine should support reusable scenario patterns. + +### 12.1 Broadcast Access Pattern + +Used for distributing payloads to many participants. + +Flow: + +1. Publish payload. +2. Grant access. +3. Notify participants. +4. Observe access and interaction. +5. Remind non-accessors. +6. Escalate unresolved cases. +7. Close when result threshold is met. + +### 12.2 Request / Collect Pattern + +Used for collecting payloads or responses. + +Flow: + +1. Publish request. +2. Notify participants. +3. Provide submission surface. +4. Validate inbound payloads. +5. Request corrections. +6. Close when required submissions are valid. + +### 12.3 Consent / Acceptance Pattern + +Used for AGBs, policy changes, and consent collection. + +Flow: + +1. Publish canonical terms version. +2. Notify affected participants. +3. Require authenticated access. +4. Record accept, decline, or no-response. +5. Enforce deadline consequences. +6. Archive evidence. + +### 12.4 Contract Closure Pattern + +Used for multi-party agreements. + +Flow: + +1. Publish contract draft. +2. Grant access to parties. +3. Verify signer identity and authority. +4. Collect signatures. +5. Counter-sign if needed. +6. Archive final signed payload. +7. Notify completion. + +### 12.5 Payment Collection Pattern + +Used for invoices, subscriptions, and dunning. + +Flow: + +1. Publish invoice or payment request. +2. Notify payer. +3. Provide payment action surface. +4. Observe payment lifecycle. +5. Remind failed or overdue payers. +6. Escalate to dunning or manual handling. +7. Close when payment is settled. + +### 12.6 Alert / Acknowledgement Pattern + +Used for incidents or urgent notices. + +Flow: + +1. Send multi-channel alert. +2. Require acknowledgement. +3. Escalate until acknowledged or expired. +4. Record time-to-awareness and response evidence. + +## 13. Functional Requirements + +### 13.1 Coordination Case Management + +The system shall allow clients to create, read, update, cancel, expire, and close CoordinationCases. + +The system shall support case-level states: + +* draft +* active +* paused +* partially_completed +* completed +* failed +* expired +* cancelled +* manually_closed + +### 13.2 Participant Management + +The system shall allow each case to define one or more participants. + +The system shall support participant roles, endpoints, required outcomes, and participant-level state. + +The system shall support participant-level closure independent of case-level closure. + +### 13.3 Intended Result Definition + +The system shall support structured intended result definitions. + +The system shall support result predicates based on participant state, evidence level, payload state, interaction state, payment state, signature state, deadlines, and thresholds. + +### 13.4 Payload Management + +The system shall support payload references and metadata. + +The system shall distinguish payload meaning, version, representation, and instance. + +The system shall support integrity references such as hashes where required. + +The system shall support inbound and outbound payloads. + +### 13.5 Access Management + +The system shall support access grants to participants. + +The system shall support access states such as: + +* not_granted +* granted +* used +* denied +* expired +* revoked +* delegated +* abused_or_suspicious + +### 13.6 Notification Management + +The system shall support notification attempts through adapters. + +The system shall track notification states and evidence. + +The system shall distinguish technical channel events from awareness evidence. + +The system shall support reminders and escalation attempts. + +### 13.7 Delivery Management + +The system shall support delivery tracking for payload availability, retrieval, submission, consumption, validation, and completion. + +The system shall allow delivery evidence to contribute to notification and result assessment. + +### 13.8 Interaction Tracking + +The system shall record and classify participant interactions. + +The system shall distinguish: + +* anonymous interaction +* proxy interaction +* scanner/bot interaction +* suspected participant interaction +* authenticated participant interaction +* authorized actor interaction +* result-relevant interaction + +### 13.9 Evidence Ledger + +The system shall maintain an append-only or audit-safe evidence ledger. + +The system shall store normalized evidence events. + +The system shall preserve raw event references when available. + +The system shall support derived assessments without losing original observations. + +### 13.10 Policy Evaluation + +The system shall evaluate policies based on current case state and evidence. + +The system shall generate next actions such as: + +* wait +* notify +* remind +* retry +* escalate +* switch_channel +* request_correction +* revoke_access +* extend_deadline +* manual_review +* close_success +* close_failure + +### 13.11 Adapter Interface + +The system shall define a standard adapter interface. + +Adapters shall declare: + +* supported actions +* emitted events +* normalized event mappings +* evidence strength +* identity strength +* failure modes +* latency profile +* cost profile + +### 13.12 API + +The system shall expose a headless API for: + +* Creating coordination cases +* Registering participants +* Registering payloads +* Creating access grants +* Triggering notifications +* Recording evidence events +* Querying assessments +* Fetching next actions +* Closing cases +* Registering adapters +* Defining scenario templates + +## 14. Non-Functional Requirements + +### 14.1 Headless First + +coordination-engine shall be designed as a headless service with APIs and event interfaces. User interfaces shall be built as separate applications. + +### 14.2 Scenario Composability + +The system shall allow scenario-specific applications to select only the relevant concepts from the framework. + +A simple notification scenario should not require contract-signing, payment, or document-validation concepts. + +### 14.3 Auditability + +The system shall support reconstruction of case decisions from evidence events, policies, and state transitions. + +### 14.4 Extensibility + +The system shall support new adapters, scenario patterns, result types, and evidence types without changing the core runtime. + +### 14.5 Reliability + +The system shall handle retries, idempotency, duplicate events, late events, and conflicting evidence. + +### 14.6 Security + +The system shall support secure handling of sensitive payload metadata, participant identifiers, access grants, and evidence. + +### 14.7 Privacy + +The system shall allow minimization of stored personal data and support references to external identity, document, or CRM systems. + +### 14.8 Observability + +The system shall expose operational metrics for case progress, unresolved participants, adapter failures, policy actions, escalation rates, and result satisfaction. + +### 14.9 Portability + +The framework shall not be coupled to one channel, provider, protocol, cloud, identity system, or document system. + +### 14.10 Agent-Friendliness + +The APIs, schemas, state models, and events shall be suitable for use by software agents and automation systems. + +## 15. MVP Scope + +The MVP should prove the core abstraction with one or two concrete scenario patterns. + +### 15.1 MVP Candidate Scenario A: Digital Payload Notification and Access + +The MVP shall support: + +* Create a coordination case. +* Add participants. +* Register one outbound payload. +* Grant participant-specific access. +* Send or simulate notification attempts. +* Record notification evidence. +* Record access and payload interaction events. +* Evaluate participant-level state. +* Evaluate case-level result. +* Generate reminder or closure actions. + +### 15.2 MVP Candidate Scenario B: Request / Collect + +The MVP may additionally support: + +* Publish a request for inbound payloads. +* Track participant submissions. +* Validate submitted payload status. +* Request corrections. +* Close participant state when valid submission is received. + +### 15.3 MVP Required Controllers + +The MVP shall include minimal versions of: + +* Coordination Controller +* Result Controller +* Participant Controller +* Payload Controller +* Access Controller +* Notification Controller +* Interaction Controller +* Evidence Ledger +* Policy Engine +* Adapter Interface + +### 15.4 MVP Adapters + +The MVP may initially implement simulated adapters only. + +Recommended first real adapters: + +* Email adapter +* Portal/action-surface adapter +* Webhook adapter + +### 15.5 MVP Success Criteria + +The MVP is successful when a developer can define a simple scenario-specific coordination application without hardcoding the full workflow. + +The MVP should demonstrate: + +* A case with multiple participants. +* Participant-level progress. +* Evidence-driven state transitions. +* At least one ambiguous/undef state. +* At least one follow-up action generated by policy. +* At least one successful completion based on interaction or delivery evidence. +* Traceability from final assessment back to evidence events. + +## 16. Initial Domain Vocabulary + +### Coordination + +The result-oriented orchestration of participants, payloads, access, actions, timing, evidence, and policies. + +### Communication + +The semantic exchange layer used to convey meaning, requests, instructions, context, and status. + +### Notification + +An awareness-oriented communication act. + +### Delivery + +The controlled availability, access, transfer, retrieval, submission, or consumption of a payload. + +### Payload + +The meaningful content, data, document, resource, action request, or artifact involved in a coordination case. + +### ActionSurface + +The interface or endpoint where participants can interact with the coordination case. + +### Interaction + +A meaningful action by a participant, system, agent, or intermediary. + +### Evidence + +An observed fact or signal used to assess progress, uncertainty, success, failure, or required follow-up. + +### IntendedResult + +The desired outcome the initiator wants to achieve. + +### Policy + +A set of rules that interprets evidence and determines next actions. + +## 17. Open Questions + +1. Should coordination-engine define its own DSL for intended results and policies, or start with JSON/YAML schemas plus application-side evaluation hooks? +2. Should evidence events be strictly append-only in the first version? +3. How much payload metadata should coordination-engine own versus reference externally? +4. Should access control be implemented internally or delegated to external IAM/portal systems in the MVP? +5. Should scenario templates be first-class runtime objects or code-level configuration? +6. How should conflicting evidence be represented? +7. Should participant identity and authority be modeled minimally in the MVP or deferred to adapter integrations? +8. What is the right boundary between coordination-engine and workflow engines such as BPMN systems? +9. Should the engine include built-in timers and scheduling, or emit next-action recommendations to an external scheduler? +10. What should be the first real scenario used to validate the framework? + +## 18. Risks + +### 18.1 Over-Abstraction + +The framework may become too abstract to implement efficiently. Mitigation: validate with concrete scenario patterns early. + +### 18.2 Workflow Engine Creep + +The product may drift into becoming a generic workflow engine. Mitigation: keep the core focused on coordination cases, participants, payloads, evidence, and result satisfaction. + +### 18.3 Adapter Complexity + +Adapters may become large and provider-specific. Mitigation: separate adapter capability declarations from normalized evidence events. + +### 18.4 Legal Misinterpretation + +Users may assume the system provides legal proof by default. Mitigation: model evidence levels and avoid claiming legal validity unless provided by scenario-specific integrations and policies. + +### 18.5 Privacy Risk + +Evidence tracking can become invasive. Mitigation: support data minimization, external references, retention policies, and privacy-aware configuration. + +### 18.6 Weak Evidence Misclassification + +Weak events such as email opens or anonymous clicks may be misclassified as success. Mitigation: evidence grading and scenario-specific required evidence levels. + +## 19. Acceptance Criteria + +The first useful version of coordination-engine shall allow: + +1. A client to create a CoordinationCase with an IntendedResult. +2. A client to add multiple participants. +3. A client to register payloads and access surfaces. +4. The engine to record normalized EvidenceEvents. +5. The engine to evaluate participant-level state. +6. The engine to evaluate case-level result state. +7. The engine to distinguish success, fail, partial, active, expired, and undef/uncertain states. +8. The engine to generate next actions from policy. +9. The engine to explain why a state or action was derived. +10. A scenario-specific application to use only a relevant subset of the framework. +11. At least one adapter or simulated adapter to emit evidence events. +12. At least one end-to-end scenario to close successfully based on interaction or delivery evidence. + +## 20. Strategic Positioning + +coordination-engine is not merely an email system, notification service, document delivery tracker, workflow engine, or campaign automation tool. + +It is a generalized digital coordination runtime. + +Its core value lies in combining: + +* Result-driven coordination +* Multi-party participant state +* Controlled payload access +* Notification and delivery abstraction +* Interaction evidence +* Policy-driven follow-up +* Adapter-based extensibility +* Headless application generation + +This makes it suitable as a foundational engine for building specialized communication and coordination applications across business, legal, operational, and technical domains. + diff --git a/spec/RssAdapterSpecification.md b/spec/RssAdapterSpecification.md new file mode 100644 index 0000000..f9f35d9 --- /dev/null +++ b/spec/RssAdapterSpecification.md @@ -0,0 +1,1394 @@ +# RssAdapterSpecification.md + +## 1. Document Status + +**Document:** RssAdapterSpecification.md +**Project:** rss-connect +**Target Integration:** coordination-engine +**Adapter Contract:** AdapterInterfaceSpecification.md v1.0 +**Status:** Draft v1.0 +**Primary Scope:** RSS, Atom, feed publication, feed fetch evidence, and optional WebSub integration as a coordination-engine adapter + +## 2. Purpose + +This document specifies how `rss-connect` models feed-based communication and how it integrates with `coordination-engine`. + +`rss-connect` is an adapter for pull-based publication and subscription-style communication through RSS, Atom, and related feed technologies. It provides feed item publication, update, removal, feed validation, feed fetch observation where available, optional WebSub hub integration, feed item correlation, and evidence normalization. + +Unlike email or SMS, RSS does not primarily push a notification to a known endpoint. RSS makes an item available in a feed that feed readers, aggregators, bots, applications, or users may poll or subscribe to. Therefore, RSS usually provides weak awareness evidence but useful publication, availability, syndication, and machine-readable distribution evidence. + +The key design objective is to make feed publication useful within coordination-engine without pretending that publication or fetch equals human awareness. + +## 3. Core Principle + +RSS is primarily a publication and availability channel, not a direct recipient-delivery channel. + +The adapter MUST distinguish: + +```text +feed item publication +feed item update +feed item removal +feed availability +feed fetch by reader/aggregator/system +optional WebSub distribution +link interaction +action-surface interaction from another system +coordination result evidence +``` + +RSS feed publication may satisfy some low-assurance broadcast scenarios, but it normally does not prove that a specific participant became aware of the item. + +## 4. Architectural Role + +### 4.1 Standalone Role + +As a standalone component, `rss-connect` provides: + +* RSS feed generation +* Atom feed generation +* feed item publication +* feed item update +* feed item removal or expiry +* feed validation +* feed metadata management +* feed item correlation +* feed archive or paging support where applicable +* fetch analytics where logs are available +* optional WebSub hub publishing +* feed health diagnostics +* normalized feed evidence events +* feed item timeline and assessment + +### 4.2 coordination-engine Adapter Role + +As a coordination-engine adapter, `rss-connect` provides: + +#### Actions + +* `feed.publish_item` +* `feed.update_item` +* `feed.remove_item` +* `feed.expire_item` +* `feed.publish_feed` +* `feed.register_topic` +* `feed.notify_hub` where WebSub is used +* `notification.publish` as a generic notification action +* `notification.register_tracking_context` + +#### Signals + +* `feed.item.published` +* `feed.item.updated` +* `feed.item.removed` +* `feed.item.expired` +* `feed.item.fetched` +* `feed.feed.fetched` +* `feed.feed.validated` +* `feed.feed.validation_failed` +* `feed.hub.notified` +* `feed.hub.notification_failed` +* `webhook.request.sent` +* `webhook.response.accepted` +* `webhook.response.rejected` +* `interaction.unverified_actor_interaction` +* `system.provider.degraded` +* `system.provider.unavailable` + +## 5. Relationship to coordination-engine + +`rss-connect` does not own: + +* `CoordinationCase` +* participant-level success +* case-level success +* awareness proof +* identity-bound interaction +* payload retrieval from portals +* payment, signature, contract, or acceptance logic +* follow-up and escalation policy + +`rss-connect` owns: + +* feed generation +* feed item publication +* feed item metadata +* feed item update and removal +* feed format validation +* feed item correlation +* WebSub publishing where supported +* feed fetch evidence where observable +* feed health diagnostics +* RSS/Atom-native evidence mapping + +The boundary rule is: + +> rss-connect reports publication, availability, feed fetch, and optional subscription-distribution evidence. coordination-engine decides what that means for the coordination case. + +## 6. RSS as Publication, Not Direct Notification + +Within `coordination-engine`, RSS is best understood as a **publication channel** or **low-assurance notification surface**. + +It may be useful for: + +* public updates +* low-assurance announcements +* machine-readable status feeds +* open information distribution +* broad, non-personalized broadcast +* API-adjacent syndication +* public audit trails +* delayed or optional awareness +* low-cost publication fallback +* feed-based integration with aggregators + +RSS is usually not suitable by itself for: + +* high-assurance legal notice +* proof of participant-specific awareness +* identity-bound acceptance +* contract closure +* payment confirmation +* confidential document delivery +* private payload transmission + +RSS can point to an action surface, but the result-relevant interaction should normally be observed by another adapter. + +Example: + +```text +rss-connect: + feed.item.published + feed.item.fetched + +portal-connect: + identity.actor_authenticated + delivery.payload.viewed + +coordination-engine: + participant result satisfied +``` + +## 7. Feed Technology Scope + +### 7.1 RSS 2.0 + +RSS 2.0 is an XML feed format with a channel and items. Items may include elements such as title, link, description, guid, pubDate, category, author, comments, enclosure, and source. RSS also includes channel-level metadata such as title, link, description, lastBuildDate, pubDate, ttl, and other optional elements. + +RSS is widely implemented but has ambiguities and loose semantics. `rss-connect` MUST avoid overinterpreting RSS reader behavior. + +### 7.2 Atom + +Atom is an XML-based syndication format standardized as RFC 4287. Atom feeds contain entries, each with metadata such as `id`, `title`, `updated`, links, authors, summaries, and content. Atom has stronger identity and update semantics than RSS and SHOULD be supported where robust feed item identity and update tracking are important. + +### 7.3 Feed Paging and Archiving + +RFC 5005 defines feed paging, archive feeds, and complete feeds. `rss-connect` MAY support these patterns for scenarios where feed history and reconstructability matter. + +### 7.4 WebSub + +WebSub is a W3C recommendation that allows subscribers to receive feed updates through a hub-based publish/subscribe pattern. `rss-connect` MAY support WebSub to improve timeliness, but WebSub delivery to subscribers still does not prove human awareness. + +## 8. RSS / Feed Lifecycle Model + +`rss-connect` models feed communication as a lifecycle with observable phases. + +```text +feed.created +feed.validated +feed.validation_failed +feed.published +feed.updated +feed.unavailable +feed.degraded + +item.created +item.rendered +item.render_failed +item.published +item.updated +item.removed +item.expired +item.archived + +hub.discovered +hub.notified +hub.notification_failed +hub.subscription_confirmed +hub.distribution_attempted + +feed.fetched +item.fetched +item.link_clicked +item.action_surface_opened +``` + +The lifecycle is not direct recipient transport. It represents publication and possible downstream consumption. + +## 9. Feed, Item, Attempt, and Subscriber Model + +The adapter MUST distinguish at least four layers. + +### 9.1 Feed + +A feed is the publication surface. + +```yaml +Feed: + feed_id: string + feed_type: rss2 | atom | json_feed | other + feed_url: string + title: string + description: string? + language: string? + owner_ref: string? + visibility: public | private | restricted | internal + format_version: string? + self_link: string? + hub_links: + - string + ttl_seconds: integer? + archive_policy_ref: string? + created_at: timestamp + updated_at: timestamp +``` + +### 9.2 FeedItem + +A feed item is the published unit. + +```yaml +FeedItem: + feed_item_id: string + feed_id: string + coordination_case_id: string? + participant_id: string? + payload_ref: ResourceRef? + action_surface_ref: ResourceRef? + item_title: string + item_summary: string? + item_content_ref: string? + canonical_url: string? + feed_item_url: string? + guid_or_id: string + published_at: timestamp + updated_at: timestamp? + expires_at: timestamp? + visibility: public | private | restricted | internal + state: FeedItemState + tracking_context: TrackingContext +``` + +### 9.3 FeedPublicationAttempt + +A publication attempt represents one attempt to publish or update a feed or item. + +```yaml +FeedPublicationAttempt: + attempt_id: string + feed_id: string + feed_item_id: string? + action_type: feed.publish_item | feed.update_item | feed.remove_item | feed.publish_feed | feed.notify_hub + state: FeedPublicationState + adapter_operation_id: string? + provider_operation_id: string? + created_at: timestamp + completed_at: timestamp? +``` + +### 9.4 FeedSubscriber / FeedConsumer + +A feed subscriber or consumer may be known or unknown. + +```yaml +FeedConsumer: + consumer_id: string? + consumer_type: human_reader | feed_reader | aggregator | search_engine | bot | system | unknown + endpoint_ref: EndpointRef? + user_agent: string? + ip_address: string? + known_participant_id: string? + confidence: low | medium | high + metadata: object? +``` + +Most RSS consumption is not identity-bound. The adapter SHOULD treat feed consumers as unknown unless authenticated feed access, subscriber registration, or other reliable correlation exists. + +## 10. Feed Item States + +The adapter SHOULD support these item states: + +```text +draft +rendered +render_failed +published +updated +removed +expired +archived +unavailable +unknown +``` + +These states are feed-native. They are not coordination result states. + +## 11. Feed Evidence Assessment + +`rss-connect` should provide a feed-native assessment separate from coordination-engine state. + +```yaml +RssEvidenceAssessment: + feed_item_id: string + coordination_case_id: string? + participant_id: string? + category: success | fail | undef + subclass: string + confidence: low | medium | high + strongest_signal: string? + evidence_summary: + - string + recommended_coordination_interpretation: string? +``` + +The assessment categories are adapter-level hints. + +### 11.1 RSS Adapter Success + +RSS-level `success` means the feed channel successfully completed a feed operation. + +Possible subclasses: + +```text +success.item_published +success.item_updated +success.item_removed +success.feed_valid +success.hub_notified +success.feed_fetched +success.item_fetched +success.link_clicked +``` + +Important: These are not automatically coordination success. + +`success.item_published` usually maps to publication availability, not participant awareness. + +### 11.2 RSS Adapter Fail + +RSS-level `fail` indicates feed publication, validation, availability, or hub notification failed. + +Subclasses: + +```text +fail.feed_invalid +fail.item_render_failed +fail.publish_failed +fail.update_failed +fail.remove_failed +fail.feed_unavailable +fail.hub_notification_failed +fail.hub_rejected +fail.invalid_guid_or_id +fail.invalid_url +fail.payload_reference_invalid +fail.visibility_policy_violation +``` + +### 11.3 RSS Adapter Undef + +RSS-level `undef` is used when publication exists but consumption or awareness is uncertain. + +Subclasses: + +```text +undef.published_but_unfetched +undef.feed_fetched_by_unknown_consumer +undef.item_fetched_by_unknown_consumer +undef.aggregator_fetch +undef.bot_fetch +undef.feed_reader_fetch +undef.identity_uncertain +undef.awareness_unproven +undef.link_clicked_identity_uncertain +undef.hub_distribution_unknown +undef.cache_interference +undef.conflicting_evidence +undef.channel_degraded +``` + +For RSS, `undef` is the normal state for participant-specific awareness. + +## 12. Detailed Feed Scenario Classification + +### 12.1 Pre-Publication Scenarios + +| Scenario | RSS assessment | Normalized event | Notes | +| -------------------------------- | ---------------------------------- | ---------------------------------------------------------- | ------------------------------- | +| Missing feed URL | `fail.publish_failed` | `feed.feed.validation_failed` | No publication surface | +| Invalid XML | `fail.feed_invalid` | `feed.feed.validation_failed` | Feed unusable | +| Invalid Atom entry ID / RSS guid | `fail.invalid_guid_or_id` | `feed.feed.validation_failed` | Item identity unreliable | +| Invalid canonical URL | `fail.invalid_url` | `feed.feed.validation_failed` | Link target unusable | +| Payload ref invalid | `fail.payload_reference_invalid` | `feed.feed.validation_failed` or `delivery.payload.failed` | Item points to invalid resource | +| Render failure | `fail.item_render_failed` | `feed.feed.validation_failed` or action failure | Item cannot be generated | +| Visibility mismatch | `fail.visibility_policy_violation` | `feed.feed.validation_failed` | Private item in public feed | +| Feed store unavailable | `fail.publish_failed` | action error | Operational failure | + +### 12.2 Publication Scenarios + +| Scenario | RSS assessment | Normalized event | Notes | +| ------------------------------- | ---------------------------------------- | ----------------------------------------- | ----------------------------------- | +| Item created | `undef.published_but_unfetched` | `feed.item.published` | Publication evidence | +| Item updated | `success.item_updated` | `feed.item.updated` | Update evidence | +| Item removed | `success.item_removed` | `feed.item.removed` | Removal evidence | +| Item expired | `success.item_removed` or expired | `feed.item.expired` | No longer active | +| Feed published | `success.item_published` or feed success | `feed.item.published` or feed-level event | Feed available | +| Feed invalid after publish | `fail.feed_invalid` | `feed.feed.validation_failed` | Publication defective | +| Feed cache not refreshed | `undef.cache_interference` | metadata | Feed readers may not see update yet | +| Feed item overwritten/reordered | `undef.conflicting_evidence` | metadata | Consumer behavior uncertain | +| Duplicate GUID/ID | `fail.invalid_guid_or_id` | `feed.feed.validation_failed` | Readers may ignore or merge item | + +### 12.3 Feed Fetch Scenarios + +| Scenario | RSS assessment | Normalized event | Notes | +| ---------------------------------------- | ---------------------------------------- | --------------------------------- | --------------------------------- | +| Feed fetched by unknown client | `undef.feed_fetched_by_unknown_consumer` | `feed.feed.fetched` | Weak evidence of consumption | +| Feed fetched by known aggregator | `undef.aggregator_fetch` | `feed.feed.fetched` | May represent many users or none | +| Feed fetched by known participant system | medium evidence | `feed.feed.fetched` | Useful for system-to-system | +| Feed fetched repeatedly | weak/medium | `feed.feed.fetched` | Could be polling | +| Feed fetched by bot/crawler | `undef.bot_fetch` | `feed.feed.fetched` | Not participant awareness | +| Conditional GET / cache validation | weak | `feed.feed.fetched` with metadata | May only confirm cache check | +| No fetch observed | `undef.published_but_unfetched` | none | Logging may be incomplete | +| Fetch log unavailable | `undef.awareness_unproven` | none | Publication remains only evidence | + +### 12.4 Item Fetch / Link Interaction Scenarios + +| Scenario | RSS assessment | Normalized event | Notes | +| -------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------- | ---------------------------------------- | +| Item content fetched | `undef.item_fetched_by_unknown_consumer` | `feed.item.fetched` | Usually identity uncertain | +| Enclosure fetched | weak/medium delivery evidence | `delivery.payload.retrieved` if payload is enclosure | Actor usually unknown | +| Link clicked | `undef.link_clicked_identity_uncertain` | `interaction.unverified_actor_interaction` | Better than feed fetch, still unverified | +| Link clicked then authenticated portal login | RSS event remains weak; portal event is strong | Portal adapter provides identity | | +| Link clicked then payload downloaded | RSS contributed path evidence; delivery evidence is decisive | Delivery adapter closes result | | +| Feed reader prefetches links | `undef.bot_fetch` or cache interference | `interaction.unverified_actor_interaction` or feed fetch | Avoid overclaiming | +| Public search engine indexes item | `undef.bot_fetch` | feed/item fetch metadata | Not recipient awareness | + +### 12.5 WebSub Scenarios + +| Scenario | RSS assessment | Normalized event | Notes | +| --------------------------------- | -------------------------------- | ------------------------------------ | -------------------------- | +| Hub discovered | neutral | metadata | Capability event | +| Hub notified successfully | `success.hub_notified` | `feed.hub.notified` | Distribution attempt began | +| Hub notification failed | `fail.hub_notification_failed` | `feed.hub.notification_failed` | Push path failed | +| Subscriber callback accepted | weak/medium system evidence | `webhook.response.accepted` | System accepted update | +| Subscriber callback rejected | negative system evidence | `webhook.response.rejected` | Push delivery failed | +| Hub status unknown | `undef.hub_distribution_unknown` | metadata | Common uncertainty | +| Authenticated WebSub distribution | stronger system evidence | webhook event with identity metadata | Still not human awareness | + +WebSub improves timeliness and system-level delivery evidence but still usually does not prove human awareness. + +## 13. Adapter-to-Coordination Mapping + +### 13.1 Core Mapping Table + +| Feed-native event | RSS assessment | coordination-engine event | Coordination interpretation | +| ---------------------------- | ---------------------------------------- | ------------------------------------------ | ---------------------------------- | +| Feed item created | `undef.published_but_unfetched` | `feed.item.published` | Publication evidence | +| Feed item updated | `success.item_updated` | `feed.item.updated` | Update evidence | +| Feed item removed | `success.item_removed` | `feed.item.removed` | Removal evidence | +| Feed item expired | expired | `feed.item.expired` | Publication no longer active | +| Feed valid | `success.feed_valid` | `feed.feed.validated` | Feed structurally usable | +| Feed invalid | `fail.feed_invalid` | `feed.feed.validation_failed` | Publication defective | +| Feed fetched | `undef.feed_fetched_by_unknown_consumer` | `feed.feed.fetched` | Weak consumption evidence | +| Item fetched | `undef.item_fetched_by_unknown_consumer` | `feed.item.fetched` | Weak/medium, identity uncertain | +| Enclosure fetched | medium payload evidence | `delivery.payload.retrieved` | Actor often unknown | +| Link clicked | `undef.link_clicked_identity_uncertain` | `interaction.unverified_actor_interaction` | Identity uncertain | +| Hub notified | `success.hub_notified` | `feed.hub.notified` | Push notification to hub attempted | +| Hub failed | `fail.hub_notification_failed` | `feed.hub.notification_failed` | Hub path failed | +| Subscriber callback accepted | system evidence | `webhook.response.accepted` | System accepted update | +| Subscriber callback rejected | failure | `webhook.response.rejected` | System rejected update | + +### 13.2 Coordination Undef Subclasses + +`coordination-engine` may derive these uncertainty classes from RSS evidence: + +```text +undef.published_but_awareness_unproven +undef.technical_publication_only +undef.no_fetch_observed +undef.feed_fetched_by_unknown_consumer +undef.identity_uncertain +undef.cache_interference +undef.hub_distribution_unknown +undef.channel_suspicious +undef.conflicting_evidence +undef.delivery_pending +undef.escalation_required +``` + +RSS evidence commonly produces: + +```text +undef.technical_publication_only +undef.feed_fetched_by_unknown_consumer +undef.identity_uncertain +undef.cache_interference +``` + +## 14. Evidence Grading Rules + +### 14.1 Feed Item Published + +```yaml +event_type: feed.item.published +evidence_grade: + strength: weak + actor_certainty: none + authority_certainty: none + payload_certainty: medium + interaction_certainty: none + timing_certainty: high + channel_certainty: medium + non_repudiation_strength: low +notes: + - Item was made available in a feed. + - Does not prove that a participant fetched, saw, or acted on the item. +``` + +### 14.2 Feed Validated + +```yaml +event_type: feed.feed.validated +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: medium + interaction_certainty: none + timing_certainty: medium + channel_certainty: high + non_repudiation_strength: low +notes: + - Feed structure is usable by feed clients. + - Validation is operational evidence, not participant evidence. +``` + +### 14.3 Feed Fetched by Unknown Consumer + +```yaml +event_type: feed.feed.fetched +evidence_grade: + strength: weak + actor_certainty: low + authority_certainty: none + payload_certainty: low + interaction_certainty: low + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: none +notes: + - A client fetched the feed. + - The client may be an aggregator, bot, cache, feed reader, or participant system. + - Does not prove human awareness. +``` + +### 14.4 Item or Enclosure Fetched + +```yaml +event_type: feed.item.fetched +evidence_grade: + strength: medium + actor_certainty: low + authority_certainty: none + payload_certainty: medium + interaction_certainty: medium + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: none +notes: + - A specific item or enclosure was fetched. + - Actor identity is usually uncertain unless authenticated access is used. +``` + +### 14.5 Link Click + +```yaml +event_type: interaction.unverified_actor_interaction +evidence_grade: + strength: medium + actor_certainty: low + authority_certainty: none + payload_certainty: medium + interaction_certainty: medium + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: none +notes: + - A link associated with the feed item was followed. + - Stronger evidence should come from the action surface or identity adapter. +``` + +### 14.6 WebSub Hub Notified + +```yaml +event_type: feed.hub.notified +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: medium + interaction_certainty: none + timing_certainty: high + channel_certainty: medium + non_repudiation_strength: low +notes: + - Hub notification was attempted or accepted. + - Subscriber delivery and human awareness are not guaranteed. +``` + +### 14.7 Subscriber Callback Accepted + +```yaml +event_type: webhook.response.accepted +evidence_grade: + strength: medium + actor_certainty: medium + authority_certainty: low + payload_certainty: medium + interaction_certainty: low + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - A subscriber system accepted a WebSub delivery callback. + - This may be strong system-to-system evidence but not human awareness. +``` + +## 15. Feed Validation Requirements + +`rss-connect` SHOULD validate generated feeds before publication. + +Validation checks SHOULD include: + +```text +well-formed XML +valid RSS or Atom structure +required channel/feed metadata present +item has required title/description or Atom-required fields +stable guid or Atom id present +valid link URLs +valid pubDate / updated timestamps +no duplicate active GUID/ID unless update semantics require it +visibility policy respected +payload/action-surface links valid +feed size within configured limits +character encoding valid +``` + +For Atom, entries should use stable IDs and update timestamps consistent with Atom semantics. + +For RSS, GUIDs should be stable and not accidentally regenerated for the same logical item unless a new item is intended. + +## 16. Adapter Descriptor + +`rss-connect` MUST expose an `AdapterDescriptor` compatible with AdapterInterfaceSpecification.md v1.0. + +```yaml +adapter_id: rss-connect.default +adapter_name: rss-connect +adapter_version: 1.0.0 +adapter_contract_version: 1.0 +adapter_types: + - notification + - communication + - feed +provider_family: feed +provider_name: rss-atom +deployment_mode: external +supported_channels: + - rss + - atom + - websub +supported_endpoint_types: + - rss_feed + - atom_feed + - websub_topic + - webhook_url +supported_actions: + - action_type: feed.publish_item + mode: async + idempotency_required: true + - action_type: feed.update_item + mode: async + idempotency_required: true + - action_type: feed.remove_item + mode: async + idempotency_required: true + - action_type: feed.notify_hub + mode: async + idempotency_required: true + - action_type: notification.publish + mode: async + idempotency_required: true + - action_type: notification.register_tracking_context + mode: sync + idempotency_required: true +emitted_event_types: + - feed.item.published + - feed.item.updated + - feed.item.removed + - feed.item.expired + - feed.item.fetched + - feed.feed.fetched + - feed.feed.validated + - feed.feed.validation_failed + - feed.hub.notified + - feed.hub.notification_failed + - webhook.request.sent + - webhook.response.accepted + - webhook.response.rejected + - interaction.unverified_actor_interaction + - system.provider.degraded + - system.provider.unavailable +evidence_profile: + strongest_evidence_level: weak_to_medium + can_prove_human_awareness: false + can_prove_payload_delivery: false + can_prove_identity: false +identity_profile: + identity_strength: none_to_low + authority_strength: none +limitations: + - Feed publication does not prove participant awareness. + - Feed fetches are often performed by aggregators, bots, caches, or feed readers. + - RSS/Atom readers may poll at unpredictable intervals. + - Feed clients may cache, reorder, merge, or ignore items. + - GUID or ID stability is essential for reliable item tracking. + - WebSub improves timeliness but does not prove human awareness. + - Public feeds are generally unsuitable for confidential payloads. +``` + +## 17. Action Request Handling + +### 17.1 `feed.publish_item` + +`feed.publish_item` publishes a new feed item. + +Required fields: + +```text +request_id +action_type +coordination_case_id +payload_ref or content +feed target +tracking_context +idempotency_key +requested_at +``` + +Example: + +```yaml +request_id: req_001 +action_type: feed.publish_item +coordination_case_id: case_123 +participant_id: null +channel: rss +target_endpoint: + endpoint_type: rss_feed + value: https://example.com/feed.xml +content: + subject: New document available + body_text: A new document is available in the portal. + action_links: + - link_id: portal_link + purpose: open_action_surface + url: https://portal.example.com/access/abc + tokenized: false + requires_authentication: true +payload_ref: + ref_type: coordination_payload + ref_id: payload_777 +tracking_context: + correlation_id: corr_789 + coordination_case_id: case_123 + payload_id: payload_777 + action_surface_id: portal_001 +idempotency_key: case_123:rss:feeditem:payload_777 +requested_at: 2026-01-01T12:00:00Z +``` + +### 17.2 Action Result + +The adapter returns: + +```yaml +request_id: req_001 +adapter_id: rss-connect.default +accepted: true +action_state: accepted +adapter_operation_id: rssop_001 +initial_events: + - event_type: feed.item.published +received_at: 2026-01-01T12:00:01Z +``` + +This result proves publication attempt success, not participant awareness. + +## 18. Feed Format Model + +`rss-connect` SHOULD support a provider-neutral feed model and render to RSS and Atom. + +```yaml +SyndicationFeed: + feed_id: string + feed_type: rss2 | atom + title: string + description: string? + site_url: string + feed_url: string + language: string? + updated_at: timestamp + ttl_seconds: integer? + hub_links: + - string + entries: + - SyndicationEntry +``` + +```yaml +SyndicationEntry: + entry_id: string + stable_id: string + title: string + summary: string? + content_ref: string? + canonical_url: string? + published_at: timestamp + updated_at: timestamp? + authors: + - string + categories: + - string + enclosures: + - FeedEnclosure + metadata: object? +``` + +```yaml +FeedEnclosure: + url: string + mime_type: string? + length_bytes: integer? + payload_ref: ResourceRef? +``` + +### RSS Rendering Notes + +RSS rendering should map: + +```text +stable_id → guid +published_at → pubDate +canonical_url → link +summary/content → description +feed updated_at → lastBuildDate +ttl_seconds → ttl, where supported +``` + +### Atom Rendering Notes + +Atom rendering should map: + +```text +stable_id → atom:id +updated_at → atom:updated +published_at → atom:published, if used +canonical_url → atom:link rel="alternate" +feed_url → atom:link rel="self" +summary/content → atom:summary or atom:content +``` + +## 19. Fetch Observation Model + +Fetch observation is optional and depends on access to server logs, reverse proxy logs, analytics, authenticated feed endpoints, or WebSub subscriber data. + +```yaml +FeedFetchEvent: + fetch_event_id: string + feed_id: string + feed_item_id: string? + occurred_at: timestamp + request_url: string + ip_address: string? + user_agent: string? + http_method: string? + status_code: integer? + cache_status: hit | miss | revalidated | unknown + conditional_request: boolean? + consumer_classification: human_reader | feed_reader | aggregator | bot | system | unknown + participant_id: string? + confidence: low | medium | high +``` + +Fetch observation MUST be classified conservatively. + +A feed fetch by a feed reader is not the same as a human reading the item. + +## 20. Consumer Classification + +`rss-connect` SHOULD classify feed consumers where possible. + +Possible consumer classifications: + +```text +known_participant_system +known_internal_system +feed_reader +aggregator +bot +crawler +search_engine +cache +websub_hub +unknown +``` + +Classification hints: + +* user-agent +* IP address +* authenticated feed access +* tokenized feed URLs +* WebSub subscription metadata +* known internal client IDs +* request pattern +* HTTP headers +* referrer +* access logs +* API key or bearer token where used + +Unless authenticated or strongly correlated, actor certainty should remain low. + +## 21. WebSub Model + +If WebSub is supported, `rss-connect` SHOULD model hubs, topics, and subscriber callbacks. + +```yaml +WebSubTopic: + topic_url: string + feed_id: string + hub_urls: + - string + lease_seconds: integer? +``` + +```yaml +WebSubNotification: + notification_id: string + topic_url: string + hub_url: string + feed_item_id: string? + state: created | sent | accepted | rejected | failed | unknown + occurred_at: timestamp + raw_response_ref: string? +``` + +WebSub events should map to: + +```text +feed.hub.notified +feed.hub.notification_failed +webhook.request.sent +webhook.response.accepted +webhook.response.rejected +``` + +WebSub improves distribution mechanics but does not change the core evidence rule: hub or subscriber delivery is not human awareness. + +## 22. Publication and Visibility Model + +`rss-connect` MUST protect against accidental leakage of private payloads. + +```yaml +FeedVisibilityPolicy: + visibility: public | private | restricted | internal + allowed_payload_sensitivity: + - public + - internal + - confidential + require_authentication: boolean + allow_tokenized_urls: boolean + allow_payload_enclosures: boolean + allow_inline_content: boolean +``` + +Public feeds SHOULD NOT contain confidential payloads. + +For confidential coordination cases, feed items should normally contain only non-sensitive notification text and a link to an authenticated action surface. + +## 23. Channel Health + +`rss-connect` SHOULD expose feed channel health. + +```yaml +RssChannelHealth: + feed_id: string + status: healthy | degraded | failing | unknown + feed_url: string + last_successful_publish_at: timestamp? + last_successful_validation_at: timestamp? + last_fetch_observed_at: timestamp? + validation_status: valid | invalid | unknown + availability_status: available | unavailable | degraded | unknown + hub_status: healthy | degraded | unavailable | not_configured | unknown + known_degradations: + - string +``` + +Health-related normalized events: + +```text +system.provider.degraded +system.provider.unavailable +system.adapter.health_changed +feed.feed.validation_failed +``` + +## 24. Security Requirements + +`rss-connect` MUST: + +* validate feed content before publication +* prevent publication of private payloads into public feeds unless explicitly configured +* preserve idempotency +* avoid duplicate feed items for repeated idempotency keys +* protect credentials for private feed publishing or WebSub hubs +* verify WebSub callbacks or hub interactions where applicable +* avoid leaking tracking tokens in public feed content unless policy allows it +* sanitize feed item content +* avoid XML injection and malformed feed generation +* support tenant or feed separation where applicable + +## 25. Privacy Requirements + +`rss-connect` SHOULD: + +* store content references instead of full content where possible +* support metadata-only mode +* support redaction of fetch logs +* avoid collecting unnecessary IP/user-agent data +* support configurable retention of raw fetch data +* distinguish public-feed publication from participant-specific tracking +* avoid participant-specific data in public feed URLs +* support deletion or anonymization workflows where applicable + +## 26. Reliability Requirements + +`rss-connect` MUST support: + +* idempotent publication requests +* duplicate feed item detection +* stable item IDs +* out-of-order update handling +* feed validation failures +* hub notification retry where configured +* degraded feed storage handling +* cache-related uncertainty +* correlation preservation +* dead-letter handling for unprocessable events + +A repeated `feed.publish_item` with the same idempotency key MUST NOT produce duplicate feed entries. + +## 27. Raw Event Preservation + +`rss-connect` SHOULD preserve raw publication, validation, fetch, and hub events or references to them. + +```yaml +RawFeedEventRef: + raw_event_id: string + source: adapter | feed_store | web_server | reverse_proxy | hub | validator + storage_ref: string? + received_at: timestamp + redacted: boolean +``` + +Normalized events should reference raw event data where available: + +```yaml +raw_event_ref: raw_feed_event_123 +``` + +## 28. Minimal API Surface + +`rss-connect` SHOULD expose a headless API. + +### 28.1 Adapter Contract API + +Required conceptual operations: + +```text +GET /adapter/descriptor +GET /adapter/health +POST /adapter/actions +POST /adapter/events/provider +GET /adapter/events +GET /adapter/feed-items/{id}/timeline +GET /adapter/feed-items/{id}/assessment +``` + +The actual transport may differ, but these conceptual operations should exist. + +### 28.2 Standalone API + +Useful standalone operations: + +```text +POST /feeds +GET /feeds/{id} +POST /feeds/{id}/items +PUT /feeds/{id}/items/{item_id} +DELETE /feeds/{id}/items/{item_id} +POST /feeds/{id}/validate +GET /feeds/{id}/health +GET /feeds/{id}/items/{item_id}/timeline +GET /feeds/{id}/items/{item_id}/assessment +POST /feeds/{id}/websub/notify +GET /feeds/{id}/fetch-events +``` + +## 29. Example End-to-End Flows + +### 29.1 Public Update Broadcast + +1. `coordination-engine` creates a broadcast coordination case. +2. Policy selects RSS as a low-assurance publication channel. +3. `coordination-engine` sends `feed.publish_item` to `rss-connect`. +4. `rss-connect` renders and validates feed item. +5. `rss-connect` publishes the item. +6. `rss-connect` emits `feed.item.published`. +7. Feed readers eventually fetch the feed. +8. `rss-connect` emits `feed.feed.fetched` where observable. +9. If the intended result only requires public publication, `coordination-engine` may mark the communication result satisfied. +10. If participant awareness is required, the case remains unresolved until stronger evidence appears. + +### 29.2 Feed Item to Authenticated Portal + +1. `rss-connect` publishes a feed item with a link to an authenticated action surface. +2. A user or feed reader follows the link. +3. `rss-connect` emits `interaction.unverified_actor_interaction`. +4. `portal-connect` records authenticated login. +5. `portal-connect` records payload view/download. +6. `coordination-engine` marks the relevant participant complete based on portal evidence. + +### 29.3 WebSub Distribution + +1. `rss-connect` publishes a feed item. +2. `rss-connect` notifies a WebSub hub. +3. Hub accepts notification. +4. `rss-connect` emits `feed.hub.notified`. +5. Subscriber callback acceptance may be observed. +6. `rss-connect` emits `webhook.response.accepted`. +7. `coordination-engine` treats this as system-level distribution evidence, not human awareness. + +### 29.4 Invalid Feed Publication + +1. `coordination-engine` requests feed item publication. +2. Rendered item has invalid XML or duplicate unstable ID. +3. `rss-connect` rejects publication. +4. `rss-connect` emits `feed.feed.validation_failed`. +5. `coordination-engine` triggers correction or alternate channel. + +## 30. Provider / Storage Implementation Guidance + +`rss-connect` may publish feeds through: + +```text +static file generation +object storage +web server file publication +CMS integration +headless content API +database-backed feed service +WebSub hub integration +reverse proxy / CDN integration +``` + +The implementation SHOULD avoid binding the core model to one storage backend. + +Provider-specific modules should map to the feed-native model first, then to normalized coordination events. + +```text +Storage/web/server event +→ feed-native event +→ RssEvidenceAssessment +→ EvidenceEvent for coordination-engine +``` + +## 31. Message Stream / Feed Separation + +`rss-connect` SHOULD support separate feeds for different purposes. + +Recommended feed streams: + +```text +public_news +status_updates +system_alerts +coordination_notifications +legal_or_high_assurance_public_notices +internal_machine_feed``` + +High-assurance or sensitive notifications SHOULD NOT share public feeds unless explicitly intended. + +Feed separation may affect: + +* visibility +* caching +* access controls +* item retention +* archive policy +* WebSub hub configuration +* payload link policy +* identity and tracking policy + +## 32. Legal and Compliance Disclaimer + +`rss-connect` does not by itself provide legal proof of participant-specific delivery, awareness, acceptance, signature, payment, or contract closure. + +It provides evidence from feed publication and feed consumption channels. + +Scenario-specific applications and `coordination-engine` policies may treat public publication as sufficient for some use cases, but that is a policy decision outside `rss-connect`. + +The adapter MUST avoid naming feed events in ways that imply participant-specific notification success. + +Use: + +```text +feed.item.published +feed.feed.fetched +``` + +Avoid: + +```text +recipient_notified +participant_aware +legal_notice_completed +``` + +## 33. MVP Scope + +The first useful version of `rss-connect` should implement: + +1. Adapter descriptor. +2. Adapter health endpoint. +3. `feed.publish_item`. +4. `feed.update_item`. +5. `feed.remove_item`. +6. Idempotent publication request handling. +7. RSS 2.0 feed rendering. +8. Basic Atom feed rendering if feasible. +9. Feed validation. +10. Stable GUID/ID handling. +11. Evidence event generation. +12. Feed item timeline. +13. Feed item assessment. +14. Optional basic fetch event ingestion from web server logs. +15. Mapping to AdapterInterfaceSpecification.md v1.0. + +### MVP Required Feed Events + +```text +feed.item.published +feed.item.updated +feed.item.removed +feed.feed.validated +feed.feed.validation_failed +feed.feed.fetched +feed.item.fetched +interaction.unverified_actor_interaction +system.provider.degraded +system.provider.unavailable +``` + +### MVP Acceptance Criteria + +The MVP is acceptable when it can: + +1. Accept a coordination-compatible feed publication request. +2. Render a valid RSS feed item. +3. Preserve correlation and idempotency. +4. Avoid duplicate entries for duplicate idempotency keys. +5. Emit `feed.item.published`. +6. Validate feed output. +7. Classify publication as weak publication evidence, not awareness. +8. Optionally ingest fetch logs and emit weak fetch evidence. +9. Provide a feed item timeline. +10. Provide an RSS evidence assessment. +11. Integrate with coordination-engine without overclaiming participant awareness. + +## 34. Future Extensions + +Potential future capabilities: + +* Atom-first robust feed generation +* JSON Feed support +* RFC 5005 archive and paging support +* WebSub hub publishing +* WebSub subscriber endpoint support +* authenticated private feeds +* tokenized participant-specific feeds +* feed reader fingerprinting +* advanced fetch analytics +* feed archive reconstruction +* CDN cache integration +* public notice publication pattern +* internal system-to-system feed pattern +* multilingual feed generation +* feed signing +* feed integrity manifests +* feed diffing +* per-feed retention policies +* feed-to-portal conversion tracking +* AI-assisted feed content generation +* monitoring for feed reader compatibility + +## 35. Non-Goals + +`rss-connect` is not: + +* a direct messaging platform +* a high-assurance notification channel by itself +* a confidential document delivery system by default +* a proof-of-awareness system +* a CRM +* a full workflow engine +* a portal +* a payment system +* a signature system +* the owner of coordination case success + +It may integrate with such systems or be used by them. + +## 36. Summary + +`rss-connect` models RSS, Atom, and related feed technologies as publication and pull-based communication channels. + +Its job is to: + +* publish feed items +* update or remove feed items +* validate feeds +* preserve stable item identity +* optionally observe feed fetches +* optionally notify WebSub hubs +* normalize feed evidence +* expose feed diagnostics +* integrate cleanly with `coordination-engine` + +The key rule is: + +> RSS events are publication and weak consumption evidence, not participant-specific result satisfaction. rss-connect reports feed-channel facts and uncertainty. coordination-engine evaluates intended results. + diff --git a/spec/RuntimeArchitectureAndAdapterSubsystem.md b/spec/RuntimeArchitectureAndAdapterSubsystem.md new file mode 100644 index 0000000..635d500 --- /dev/null +++ b/spec/RuntimeArchitectureAndAdapterSubsystem.md @@ -0,0 +1,1776 @@ +# RuntimeArchitectureAndAdapterSubsystem.md + +## 1. Document Status + +**Document:** RuntimeArchitectureAndAdapterSubsystem.md +**Project:** coordination-engine +**Version:** 1.1 +**Status:** Updated Draft +**Scope:** Runtime architecture and adapter subsystem +**Related Documents:** + +* `INTENT.md` +* `ProductRequirementsDocument.md` +* `AdapterInterfaceSpecification.md` +* `EmailAdapterSpecification.md` +* `SmsAdapterSpecification.md` +* `RssAdapterSpecification.md` +* `XmppAdapterSpecification.md` +* `HybridmailAdapterSpecification.md` + +## 2. Version 1.1 Change Summary + +This version incorporates architectural learnings from concrete adapter specifications for email, SMS, RSS, XMPP, and hybrid mail. + +Main updates: + +1. Adapters are now modeled as semantic evidence components, not simple integration endpoints. +2. Added adapter capability resolution. +3. Added evidence ceiling evaluation. +4. Added native status mapping as an architectural concern. +5. Added endpoint quality as first-class runtime information. +6. Added late-event processing. +7. Added adapter advisory assessments. +8. Added conformance and golden-test harness concepts. +9. Strengthened weakest-safe-mapping and semantic-underclaiming rules. +10. Clarified that result interpretation requires evidence, adapter semantics, and scenario policy. + +## 3. Purpose + +`coordination-engine` is a headless runtime for digital coordination. It coordinates participants around payloads and action surfaces through notifications, deliveries, access control, interactions, evidence, and policies to achieve intended results under uncertainty. + +This document specifies the runtime architecture and adapter subsystem. + +The adapter subsystem allows `coordination-engine` to integrate with external protocols, providers, and systems without hardcoding provider-specific semantics into the core engine. + +Examples of adapters: + +```text id="pi0m7p" +email-connect +sms-connect +rss-connect +xmpp-connect +hybridmail-connect +portal-connect +payment-connect +signature-connect +document-connect +identity-connect +webhook-connect +``` + +## 4. Architectural Intent + +The runtime architecture is based on the following principles. + +### 4.1 Coordination is result-driven + +A coordination case succeeds when its intended result is satisfied, not when messages are merely sent. + +### 4.2 Digital coordination is evidence-driven + +The engine records observations from multiple systems and derives participant and case states from evidence. + +### 4.3 Adapters are semantic boundary components + +Adapters do not only execute actions and emit events. They also declare capabilities, preserve native events, map native events conservatively, expose evidence ceilings, and report limitations. + +### 4.4 External systems remain external + +The engine should orchestrate and evaluate coordination, not replace email providers, SMS providers, feed servers, XMPP servers, hybrid-mail providers, portals, payment systems, signature systems, document stores, or identity systems. + +### 4.5 Uncertainty is first-class + +Many signals are ambiguous. The architecture must represent weak, conflicting, late, missing, or uncertain evidence. + +### 4.6 Underclaiming is mandatory + +If a native provider event is ambiguous, the adapter and runtime must prefer the weakest safe interpretation. + +### 4.7 Scenario-specific applications compose selectively + +Simple use cases should not require payment, signature, portal, or hybrid-mail semantics. Complex use cases should be able to add stronger identity, authority, payload, access, and evidence requirements. + +## 5. Core Runtime Overview + +`coordination-engine` is composed of a central coordination controller, specialized domain controllers, an evidence ledger, a policy engine, and an adapter semantics boundary. + +```text id="372jsk" +coordination-engine + coordination-controller + result-controller + participant-controller + payload-controller + access-controller + notification-controller + delivery-controller + interaction-controller + identity-authority-controller + evidence-ledger + policy-engine + adapter-semantics-boundary +``` + +The main runtime object is the `CoordinationCase`. + +External systems connect through adapters. Adapters provide actions, emit events, declare capabilities, and constrain interpretation through evidence ceilings and native status mappings. + +## 6. High-Level Runtime Flow + +### 6.1 Action Flow + +```text id="19qc4b" +Policy requests action +→ Capability Resolver selects suitable adapter +→ Adapter Registry checks health and capability +→ Coordination Controller creates AdapterActionRequest +→ Adapter Action Dispatcher sends request +→ Adapter executes action +→ AdapterActionResult is recorded +→ Initial EvidenceEvents are stored +→ Later AdapterEvents are ingested asynchronously +``` + +### 6.2 Event Flow + +```text id="mzs6c8" +Adapter native event +→ Native Status Mapping +→ Normalized EvidenceEvent +→ Evidence Ledger +→ Evidence Ceiling Evaluation +→ Endpoint Quality Update +→ Adapter Assessment Update +→ Policy Evaluation +→ Participant Assessment +→ Case Assessment +→ Next Action Generation +``` + +### 6.3 Interpretation Flow + +The engine MUST interpret evidence using the following inputs: + +```text id="1l9qcr" +EvidenceEvent ++ AdapterCapabilityProfile ++ EvidenceCeiling ++ NativeStatusMapping ++ EvidenceGrade ++ ConfidenceGrade ++ EndpointQuality ++ LateEventPolicy ++ FeatureDependencies ++ Scenario Policy ++ IntendedResult +``` + +The engine MUST NOT infer stronger participant or case state than the evidence ceiling of the originating adapter allows, unless additional evidence from another adapter raises the assurance level. + +## 7. Core Runtime Model + +## 7.1 CoordinationCase + +A `CoordinationCase` is the primary aggregate. + +It represents one goal-directed coordination process. + +Example cases: + +```text id="ub0h4c" +deliver documents to recipients +collect missing documents +obtain AGB acceptance +close contracts +collect payments +gather acknowledgements +coordinate approvals +send hybrid-mail fallback letters +``` + +Minimal structure: + +```yaml id="ji6fzy" +CoordinationCase: + id: string + initiator: ActorRef + purpose: string + scenario_type: string + intended_result: IntendedResult + participants: + - ParticipantRef + payloads: + - PayloadRef + action_surfaces: + - ActionSurfaceRef + policies: + - PolicyRef + state: CoordinationCaseState + assessment: CoordinationAssessment + created_at: timestamp + updated_at: timestamp +``` + +## 7.2 Participant + +A `Participant` is an actor involved in a coordination case. + +Participants may be humans, organizations, systems, agents, delegates, signers, payers, approvers, respondents, recipients, or intermediaries. + +```yaml id="t2gg15" +Participant: + id: string + actor_ref: ActorRef + roles: + - string + contact_endpoints: + - EndpointRef + authority_profile_ref: string? + required_outcomes: + - RequiredOutcome + state: ParticipantState +``` + +## 7.3 IntendedResult + +An `IntendedResult` defines what the initiator wants to achieve. + +```yaml id="zmxcoo" +IntendedResult: + id: string + result_type: string + target_population: ParticipantSelector + required_outcome: string + required_evidence_level: EvidenceLevel + assurance_requirements: AssuranceRequirement? + threshold: Threshold? + deadline: timestamp? + partial_success_rules: + - RuleRef + failure_rules: + - RuleRef +``` + +Example: + +```yaml id="671wqx" +result_type: payload_access +required_outcome: pdf_downloaded +threshold: + type: percentage + value: 95 +deadline: 2026-12-31T23:59:59Z +``` + +## 7.4 AssuranceRequirement + +A scenario may define required assurance levels. + +```yaml id="kcnj5r" +AssuranceRequirement: + awareness_assurance: none | weak | medium | strong | conclusive + delivery_assurance: none | weak | medium | strong | conclusive + identity_assurance: none | weak | medium | strong | conclusive + authority_assurance: none | weak | medium | strong | conclusive + non_repudiation_assurance: none | weak | medium | strong | conclusive +``` + +The Policy Engine uses this to select adapters and interpret evidence. + +## 7.5 Payload + +A `Payload` is a meaningful resource involved in the case. + +Payloads are not limited to documents. They can include invoices, payment requests, contract drafts, signed contracts, submitted forms, data packages, receipts, terms versions, or structured messages. + +```yaml id="y8zwcf" +Payload: + id: string + semantic_role: string + payload_type: string + version: string? + representation_refs: + - RepresentationRef + integrity_hash: string? + sensitivity: string? + retention_policy_ref: string? + validation_policy_ref: string? +``` + +## 7.6 ActionSurface + +An `ActionSurface` is the place where a participant can interact with a payload or perform a required action. + +Examples: + +```text id="ekgabs" +portal page +mobile app screen +payment page +signature flow +upload form +approval screen +chatbot flow +API endpoint +email reply parser +XMPP bot +feed item +physical letter +``` + +```yaml id="dxrso5" +ActionSurface: + id: string + type: string + adapter_ref: string? + url: string? + endpoint_ref: string? + supported_interactions: + - string + access_policy_ref: string? + requires_authentication: boolean? +``` + +## 7.7 EvidenceEvent + +An `EvidenceEvent` is a normalized observation used by the engine to derive state. + +```yaml id="dnggme" +EvidenceEvent: + id: string + event_type: string + event_family: string + source_adapter: string? + source_adapter_event_id: string? + native_status_mapping_ref: string? + coordination_case_id: string + participant_id: string? + payload_ref: ResourceRef? + action_surface_ref: ActionSurfaceRef? + endpoint_ref: EndpointRef? + actor_ref: ActorRef? + occurred_at: timestamp? + observed_at: timestamp + normalized_meaning: string + confidence: ConfidenceGrade + evidence_grade: EvidenceGrade + raw_event_ref: string? + correlation: CorrelationContext + metadata: object? +``` + +Evidence events are the basis for assessment, policy evaluation, auditability, and explainability. + +## 8. Runtime Components + +## 8.1 Coordination Controller + +The `coordination-controller` is the central runtime component. + +Responsibilities: + +* Create and manage `CoordinationCase` objects. +* Coordinate specialized controllers. +* Maintain case lifecycle state. +* Request policy evaluations. +* Apply derived state transitions. +* Trigger adapter actions through the adapter subsystem. +* Close, fail, expire, pause, or escalate cases. + +The coordination controller does not directly speak to external systems. It uses adapters through the adapter semantics boundary. + +## 8.2 Result Controller + +The `result-controller` evaluates whether an intended result has been satisfied. + +Responsibilities: + +* Evaluate case-level success predicates. +* Evaluate participant-level completion. +* Handle thresholds, deadlines, partial success, failure, expiry, and manual override. +* Produce result assessment events. +* Respect scenario assurance requirements. +* Avoid interpreting adapter evidence beyond its evidence ceiling. + +Example derived states: + +```text id="4yxlwc" +active +partially_completed +completed_successfully +completed_partially +failed +expired +manually_closed +``` + +## 8.3 Participant Controller + +The `participant-controller` manages participant state within a case. + +Responsibilities: + +* Maintain participant roles and required outcomes. +* Link participants to payloads, action surfaces, endpoints, and evidence. +* Derive participant progress states. +* Support delegates, organizations, systems, and agents. +* Track per-participant unresolved/undef subclasses. + +Example participant states: + +```text id="tgrdx1" +not_started +notification_pending +notified +awareness_uncertain +access_granted +accessed +interaction_started +required_action_completed +failed +expired +escalated +completed +``` + +## 8.4 Payload Controller + +The `payload-controller` manages payload identity, versioning, representation, integrity, validation, and lifecycle references. + +Responsibilities: + +* Register payloads. +* Link payloads to participants and intended results. +* Track payload availability. +* Track inbound and outbound payload references. +* Integrate with document stores, archives, or payload providers through adapters. + +The payload controller does not need to store the payload itself. It may only store metadata and references. + +## 8.5 Access Controller + +The `access-controller` manages access to payloads and action surfaces. + +Responsibilities: + +* Create access grants. +* Revoke access. +* Track access usage, denial, expiry, delegation, and abuse. +* Coordinate with identity and authority systems. +* Emit access-related evidence events. + +Example access states: + +```text id="c4rhw3" +not_granted +granted +used +denied +expired +revoked +delegated +suspicious +``` + +## 8.6 Notification Controller + +The `notification-controller` manages awareness-oriented signals. + +Responsibilities: + +* Create notification attempts. +* Select channels according to policy and adapter capability. +* Trigger notification adapters. +* Record notification evidence. +* Distinguish technical delivery from awareness evidence. +* Support reminders, retries, and escalation. + +Notification does not equal delivery. A notification may point to an action surface or payload but does not necessarily carry the payload. + +## 8.7 Delivery Controller + +The `delivery-controller` manages payload availability, retrieval, submission, transfer, consumption, and completion. + +Responsibilities: + +* Track outbound delivery evidence. +* Track inbound delivery evidence. +* Track payload submission and validation results. +* Interpret delivery-side events as possible evidence for notification or result success. +* Distinguish availability, dispatch, handover, access, download, submission, and completion. + +Delivery is generalized as controlled payload access, transfer, retrieval, submission, or consumption. + +## 8.8 Interaction Controller + +The `interaction-controller` classifies meaningful participant actions. + +Responsibilities: + +* Record interaction events. +* Distinguish anonymous, suspected, authenticated, delegated, authorized, bot, scanner, and proxy interactions. +* Determine whether interactions are result-relevant. +* Feed interaction evidence into participant and result assessment. + +Example interactions: + +```text id="8dmkz5" +notification_opened +link_clicked +portal_opened +document_viewed +pdf_downloaded +form_started +form_submitted +payment_started +payment_settled +contract_viewed +signature_completed +acknowledgement_recorded +``` + +## 8.9 Identity & Authority Controller + +The `identity-authority-controller` evaluates actor identity and authority. + +Responsibilities: + +* Link actors to participants. +* Evaluate authentication strength. +* Evaluate authorization, delegation, and representation. +* Provide evidence grades for identity and authority. +* Integrate with IAM, SSO, MFA, signature providers, or authorization systems. + +Example identity grades: + +```text id="1cx3ei" +unknown +device_known +session_authenticated +mfa_verified +identity_provider_verified +qualified_identity_verified +``` + +Example authority grades: + +```text id="g09lft" +unknown +self_authorized +delegated +organizational_representative +authorized_signer +payment_authorized +admin_override +``` + +## 8.10 Evidence Ledger + +The `evidence-ledger` stores normalized observations. + +Responsibilities: + +* Persist evidence events. +* Preserve raw event references. +* Support idempotency and deduplication. +* Maintain correlation across adapters. +* Store native-status mapping references. +* Support late events. +* Support audit reconstruction. +* Support state derivation and explainability. + +The evidence ledger should be append-oriented. Derived state may be updated, but original evidence events should remain traceable. + +## 8.11 Policy Engine + +The `policy-engine` interprets state and evidence to determine next actions. + +Responsibilities: + +* Evaluate result policies. +* Evaluate follow-up policies. +* Evaluate escalation rules. +* Evaluate retry and timeout rules. +* Compare assurance requirements with adapter assurance capabilities. +* Use evidence ceilings to avoid overclaiming. +* Generate next-action recommendations or commands. +* Explain why actions were generated. + +Example next actions: + +```text id="zfx76u" +wait +send_notification +send_reminder +retry +switch_channel +escalate +request_correction +revoke_access +extend_deadline +manual_review +close_success +close_failure +expire_case +``` + +## 9. Adapter Semantics Boundary + +The adapter subsystem is a semantic boundary layer. + +Its purpose is to protect the coordination engine from overinterpreting external system events. + +```text id="rb4hj1" +External provider semantics +→ adapter-native model +→ conservative normalized evidence +→ coordination interpretation +``` + +Adapters do not only emit events. They also declare the maximum meaning of their events. + +The runtime MUST evaluate adapter evidence using: + +```text id="r321bb" +normalized event type +evidence grade +confidence grade +native status mapping +evidence ceiling +assurance capability +endpoint quality +feature dependencies +late-event policy +scenario result requirements +``` + +## 10. Adapter Subsystem Components + +The adapter semantics boundary consists of: + +```text id="d01xuc" +adapter-registry +capability-resolver +adapter-action-dispatcher +adapter-event-ingestor +native-status-mapping-registry +evidence-normalizer +evidence-ceiling-evaluator +endpoint-quality-store +adapter-assessment-store +adapter-health-monitor +late-event-processor +adapter-conformance-harness +``` + +## 10.1 Adapter Registry + +The Adapter Registry stores adapter descriptors. + +Responsibilities: + +* Register adapters. +* Store `AdapterDescriptor`. +* Store `AdapterCapabilityProfile`. +* Store `EvidenceCeiling`. +* Store `AssuranceCapability`. +* Store supported actions. +* Store supported event types. +* Store feature dependencies. +* Store conformance level. +* Store known limitations. + +The Adapter Registry is used by the Capability Resolver and Policy Engine. + +## 10.2 Capability Resolver + +The Capability Resolver selects suitable adapters for a desired action or assurance requirement. + +Inputs: + +```text id="qkht2m" +required action +participant endpoints +payload type +action surface +required assurance +deadline +adapter health +adapter capability +feature dependencies +cost/latency constraints +scenario policy +``` + +Outputs: + +```text id="f9motk" +candidate adapters +candidate actions +suitability ranking +reasoning +known limitations +``` + +Example: + +```text id="rqgqc0" +Need awareness >= medium: + SMS may be suitable if DLR supported. + XMPP may be suitable if displayed markers supported. + Email alone may be insufficient. +``` + +Example: + +```text id="y8qg8k" +Need delivery >= strong: + Portal authenticated download may be suitable. + Hybrid mail delivery confirmation may be suitable if product supports it. + Ordinary hybrid-mail postal handover may be insufficient. +``` + +## 10.3 Adapter Action Dispatcher + +The Adapter Action Dispatcher sends `AdapterActionRequest` objects to adapters. + +Responsibilities: + +* Build action requests. +* Include correlation context. +* Include idempotency key. +* Validate required fields. +* Dispatch to adapter. +* Capture `AdapterActionResult`. +* Store initial evidence events. +* Handle duplicate and idempotency-conflict responses. +* Handle retryable and non-retryable action errors. + +## 10.4 Adapter Event Ingestor + +The Adapter Event Ingestor receives `AdapterEvent` or `EvidenceEvent` objects. + +Responsibilities: + +* Validate event shape. +* Verify adapter identity. +* Verify webhook authenticity where applicable. +* Deduplicate events. +* Preserve raw event references. +* Pass events through evidence normalization. +* Append normalized evidence to Evidence Ledger. + +## 10.5 Native Status Mapping Registry + +The Native Status Mapping Registry stores provider-native to normalized event mappings. + +Responsibilities: + +* Store adapter-specific native status mappings. +* Store provider status semantics. +* Store weakest-safe mappings. +* Store confidence and ambiguity flags. +* Store product or feature dependencies. +* Support mapping lookup during event normalization. +* Support coding-agent inspection. + +Example: + +```text id="qrfzif" +Native email "delivered" → notification.endpoint.accepted +Native SMS "delivered" → notification.endpoint.accepted with delivery semantics +Native hybrid-mail "sent" → delivery.payload.submitted unless handover semantics are proven +``` + +## 10.6 Evidence Normalizer + +The Evidence Normalizer converts adapter-native events into normalized `EvidenceEvent` objects. + +Responsibilities: + +* Apply native status mappings. +* Apply weakest-safe-mapping rule. +* Assign evidence grade. +* Assign confidence grade. +* Preserve native status. +* Preserve raw event reference. +* Attach correlation context. +* Attach adapter semantics metadata. + +The Evidence Normalizer must never strengthen evidence beyond the adapter’s declared mapping. + +## 10.7 Evidence Ceiling Evaluator + +The Evidence Ceiling Evaluator ensures runtime interpretation does not exceed adapter capability. + +Responsibilities: + +* Compare evidence event against adapter evidence ceiling. +* Compare event against scenario assurance requirement. +* Determine whether evidence is sufficient, insufficient, or conditional. +* Prevent overclaiming in participant and result state derivation. + +Example: + +```text id="6enfrt" +Event: + email notification.endpoint.accepted + +Evidence ceiling: + cannot prove human awareness + +Result requirement: + awareness >= medium + +Outcome: + insufficient; participant remains unresolved +``` + +Example: + +```text id="bp4o8c" +Event: + portal delivery.payload.downloaded + +Evidence ceiling: + can prove payload access if authenticated + +Result requirement: + delivery >= strong + +Outcome: + sufficient if identity evidence also meets policy +``` + +## 10.8 Endpoint Quality Store + +The Endpoint Quality Store maintains endpoint quality signals emitted or inferred from adapters. + +Examples: + +```text id="6l6e2i" +email hard bounce history +SMS opt-out state +RSS feed validity +XMPP presence / JID quality +postal address validation / return history +``` + +Responsibilities: + +* Store endpoint quality by endpoint reference. +* Update quality from evidence events. +* Expose quality to policy engine. +* Influence adapter selection. +* Avoid treating endpoint quality as result evidence by itself. + +## 10.9 Adapter Assessment Store + +Adapters may emit advisory `AdapterEvidenceAssessment` objects. + +Responsibilities: + +* Store adapter-native assessments. +* Expose assessments for diagnostics. +* Keep them separate from coordination-engine authoritative state. +* Allow policy engine to use them as hints, not final truth. + +Rule: + +```text id="7z2xe6" +AdapterEvidenceAssessment is advisory. +coordination-engine remains authoritative for participant and case state. +``` + +Example: + +```text id="g0pn77" +sms-connect assessment: + success.delivered + +coordination-engine assessment: + undef.delivered_but_awareness_unproven +``` + +Both may be correct at different semantic layers. + +## 10.10 Adapter Health Monitor + +The Adapter Health Monitor tracks adapter and provider health. + +Responsibilities: + +* Poll adapter health. +* Store health state. +* Detect degradation. +* Feed health into capability resolution. +* Trigger policy fallback where needed. + +Health examples: + +```text id="vv4o3s" +email provider degraded +SMS route unavailable +RSS feed invalid +XMPP stream disconnected +Hybrid-mail provider status API unavailable +``` + +## 10.11 Late Event Processor + +The Late Event Processor handles events that arrive after state has already progressed. + +Examples: + +```text id="y8ro1d" +email async bounce +SMS late delivery receipt +RSS delayed fetch log +XMPP delayed delivery receipt +Hybrid-mail return mail after weeks +``` + +Responsibilities: + +* Accept late events. +* Append them to the evidence ledger. +* Decide whether to trigger reassessment. +* Annotate existing participant/case state. +* Reopen participant/case where policy allows. +* Trigger compensating actions or manual review. +* Preserve the event even if policy chooses not to change state. + +Rule: + +```text id="io3lmx" +Late events are never discarded merely because a participant or case already has a derived state. +``` + +## 10.12 Adapter Conformance Harness + +The Adapter Conformance Harness validates adapter behavior against the adapter interface and golden tests. + +Responsibilities: + +* Validate `AdapterDescriptor`. +* Validate capability profile. +* Validate evidence ceiling. +* Validate required actions. +* Validate event mappings. +* Validate idempotency behavior. +* Validate weakest-safe-mapping rule. +* Validate golden tests. +* Detect overclaiming. + +Example golden test: + +```text id="fzwdv0" +Email provider says "delivered": + MUST emit notification.endpoint.accepted. + MUST NOT emit interaction.notification.opened. +``` + +Example golden test: + +```text id="h6r30q" +Hybrid-mail ordinary letter handed over: + MUST emit delivery.postal.handed_over. + MUST NOT emit delivery.postal.delivery_confirmed. +``` + +## 11. Adapter Architecture + +## 11.1 Adapter Definition + +An adapter is a component that connects `coordination-engine` with an external protocol, provider, system, or technology. + +An adapter has these roles: + +```text id="0kboym" +Adapter = + Action Provider + + Signal Provider + + Capability Descriptor + + Evidence Normalizer + + Evidence Ceiling Declaration + + Endpoint Quality Source + + Advisory Assessment Source + + Health / Diagnostics Source +``` + +## 11.2 Action Provider + +As an action provider, the adapter performs operations requested by `coordination-engine`. + +Examples: + +```text id="y8rvcf" +send email +send SMS +publish RSS item +send XMPP message +submit hybrid-mail letter +grant portal access +create payment request +create signature envelope +revoke access token +archive document +request identity verification +``` + +## 11.3 Signal Provider + +As a signal provider, the adapter emits observations. + +Examples: + +```text id="8jc1dw" +email provider accepted message +email hard bounce received +SMS delivery receipt received +RSS feed item fetched +XMPP delivery receipt received +hybrid-mail return mail received +portal login completed +document downloaded +payment settled +signature completed +``` + +## 11.4 Capability Descriptor + +As a capability descriptor, the adapter declares what it can do and what evidence it can provide. + +This allows `coordination-engine` to reason about which adapters are suitable for a scenario. + +## 11.5 Evidence Ceiling Declaration + +As an evidence ceiling source, the adapter declares the strongest meaning its events can support. + +Examples: + +```text id="61vu71" +email cannot prove human awareness +SMS delivered does not prove human read +RSS publication does not prove participant awareness +XMPP displayed marker does not prove comprehension +Hybrid-mail handover does not prove final delivery +``` + +## 12. Adapter Contract + +The adapter contract is defined in `AdapterInterfaceSpecification.md`. + +At runtime, the engine expects: + +```text id="os2p7w" +AdapterDescriptor +AdapterCapabilityProfile +AdapterActionCapability +AdapterActionRequest +AdapterActionResult +AdapterEvent +EvidenceEvent +AdapterEvidenceAssessment +EvidenceGrade +EvidenceCeiling +NativeStatusMapping +EndpointQuality +AdapterHealth +CorrelationContext +LateEventPolicy +FeatureDependency +AssuranceCapability +``` + +## 13. Event Normalization + +## 13.1 Native Events vs Normalized Events + +External systems use different event vocabularies. The adapter layer is responsible for normalizing them. + +Example: + +```text id="9g284p" +SendGrid delivered +Mailgun delivered +SES Delivery +Postmark Delivery +``` + +These may all map to: + +```text id="pqhamo" +notification.endpoint.accepted +``` + +with meaning: + +```text id="43anbf" +recipient email server accepted the message +``` + +They must not map directly to business-level success. + +## 13.2 Normalized Event Families + +The engine defines canonical event families. + +```text id="iw23ra" +coordination +participant +payload +access +notification +publication +delivery +interaction +identity +authority +payment +signature +feed +policy +result +system +manual +``` + +## 13.3 Adapter Examples + +### Email + +```text id="pwlz8d" +email.provider.accepted +→ notification.attempt.accepted_by_provider + +email.transport.mx_accepted +→ notification.endpoint.accepted + +email.transport.hard_bounce +→ notification.endpoint.rejected_permanent + +email.open.proxy_like +→ interaction.proxy_or_privacy_interaction + +email.click.scanner_like +→ interaction.scanner_or_bot_interaction +``` + +### SMS + +```text id="26js3x" +sms.provider.accepted +→ notification.attempt.accepted_by_provider + +sms.carrier.accepted +→ notification.endpoint.accepted + +sms.delivered +→ notification.endpoint.accepted with device/carrier metadata + +sms.undelivered +→ notification.endpoint.rejected_permanent or temporary + +sms.reply.ack +→ interaction.acknowledgement_recorded +``` + +### RSS + +```text id="06c5vv" +rss.item.published +→ feed.item.published + +rss.feed.fetched +→ feed.feed.fetched + +rss.link.clicked +→ interaction.unverified_actor_interaction +``` + +### XMPP + +```text id="mnjxnz" +xmpp.stream.ack +→ notification.attempt.accepted_by_provider + +xmpp.delivery_receipt +→ notification.endpoint.accepted + +xmpp.displayed_marker +→ interaction.notification.opened + +xmpp.reply +→ interaction.reply_received +``` + +### Hybrid Mail + +```text id="69qicl" +hybridmail.upload.accepted +→ delivery.payload.accepted + +hybridmail.validation.failed +→ payload.validation_failed + +hybridmail.production.completed +→ delivery.production.completed + +hybridmail.postal.handed_over +→ delivery.postal.handed_over + +hybridmail.return.received +→ delivery.postal.return_received +``` + +## 14. Case State Derivation + +The engine derives state from evidence, adapter semantics, and policy. + +## 14.1 Participant Assessment + +A participant assessment may include: + +```yaml id="rri7qg" +ParticipantAssessment: + participant_id: string + state: string + notification_state: string? + access_state: string? + delivery_state: string? + interaction_state: string? + result_state: string? + evidence_level: EvidenceLevel + assurance_level: AssuranceCapability? + uncertainty_class: string? + risk_level: string? + next_actions: + - NextAction +``` + +Example uncertainty classes: + +```text id="gfeidz" +undef.pending +undef.technical_acceptance_only +undef.no_signal +undef.weak_positive +undef.identity_uncertain +undef.channel_suspicious +undef.conflicting_evidence +undef.delivery_pending +undef.escalation_required +undef.handed_to_postal_service_but_delivery_unproven +``` + +## 14.2 Case Assessment + +A case assessment may include: + +```yaml id="anxw5i" +CoordinationAssessment: + case_id: string + state: string + result_satisfied: boolean + participants_total: integer + participants_completed: integer + participants_pending: integer + participants_failed: integer + participants_escalated: integer + progress_percentage: number + deadline_risk: string? + channel_risk: string? + compliance_risk: string? + next_actions: + - NextAction +``` + +## 14.3 State Derivation Rule + +Participant and case state derivation MUST use: + +```text id="jo48dd" +evidence events +adapter evidence ceiling +adapter assurance capability +native status mapping confidence +scenario policy +intended result +participant requirements +deadline and thresholds +``` + +The engine MUST NOT turn adapter-native success into coordination success without policy evaluation. + +## 15. Policy Engine + +## 15.1 Policy Inputs + +The policy engine evaluates: + +* case state +* participant states +* evidence events +* evidence grades +* confidence grades +* adapter evidence ceilings +* adapter assurance capabilities +* native status mapping confidence +* endpoint quality +* deadlines +* result predicates +* adapter health +* channel availability +* failure modes +* scenario-specific configuration + +## 15.2 Policy Outputs + +The policy engine produces: + +```yaml id="h3h8hu" +NextAction: + id: string + action_type: string + target: string + priority: string + due_at: timestamp? + adapter_action_request: AdapterActionRequest? + explanation: string +``` + +Examples: + +```text id="u60ell" +send_email_notification +send_sms_reminder +switch_to_push +grant_portal_access +submit_hybridmail_letter +request_manual_review +close_participant_success +close_case_partial +expire_case +``` + +## 15.3 Policy Example: Email Ambiguity + +```yaml id="tt143n" +if: + participant.notification_state: notification.endpoint.accepted + source_adapter: email-connect + participant.delivery_state: none + age_since_notification: ">72h" +then: + action_type: send_reminder + channel_preference: sms + explanation: Email was technically accepted but no access or interaction evidence was observed within 72 hours. +``` + +## 15.4 Policy Example: Hybrid-Mail Handover Insufficient + +```yaml id="60m9o8" +if: + participant.delivery_state: delivery.postal.handed_over + source_adapter: hybridmail-connect + result_requirement: delivery_confirmation +then: + action_type: wait_or_request_status + explanation: Postal handover is dispatch evidence but does not satisfy delivery-confirmation requirement. +``` + +## 15.5 Policy Example: Cross-Adapter Evidence Upgrade + +```yaml id="75e7e2" +if: + email_event: notification.endpoint.accepted + portal_event: delivery.payload.downloaded + portal_identity: identity.actor_authenticated +then: + action_type: close_participant_success + explanation: Email alone was weak, but authenticated portal download satisfies payload-access result. +``` + +## 16. Adapter Integration Modes + +## 16.1 Embedded Library Mode + +An adapter may be included as a library in the same deployment. + +Suitable for: + +* simple simulations +* testing +* local development +* low-volume integrations + +## 16.2 Sidecar Service Mode + +An adapter may run as a service alongside the engine. + +Suitable for: + +* provider credentials isolation +* webhook ingestion +* retry handling +* operational separation + +## 16.3 External Service Mode + +An adapter may run as an independently deployed service. + +Suitable for: + +* reusable connectors like `email-connect` +* multi-tenant environments +* shared provider integrations +* separate scaling and security boundaries + +Preferred direction: + +```text id="7e1g63" +coordination-engine owns the contract. +*-connect repos implement the contract. +``` + +## 17. Adapter Repository Pattern + +Adapters should preferably live in separate repositories. + +Example: + +```text id="h3h1sf" +email-connect +sms-connect +rss-connect +xmpp-connect +hybridmail-connect +portal-connect +payment-connect +signature-connect +document-connect +identity-connect +``` + +Each adapter repository should provide: + +```text id="ev9h37" +INTENT.md +docs/AdapterImplementation.md +docs/EventMapping.md +docs/ProviderModel.md +docs/EvidenceClassification.md +docs/GoldenTests.md +schemas/ +src/ +tests/ +``` + +Each adapter should include a compatibility declaration: + +```yaml id="zfsm9g" +coordination_engine_compatibility: + adapter_contract_version: 1.1 + conformance_level: 3 + supported_actions: + - notification.send + emitted_events: + - notification.attempt.accepted_by_provider + - notification.endpoint.accepted + evidence_ceiling: + max_positive_event: interaction.unverified_actor_interaction + max_positive_strength: medium + known_limitations: + - does_not_prove_human_awareness +``` + +## 18. Data Flow Examples + +## 18.1 Notification and Access Flow + +```text id="kq1g7w" +1. Client creates CoordinationCase. +2. Client adds participants and payload. +3. Access Controller creates access grants. +4. Policy Engine requests notification. +5. Capability Resolver selects email-connect. +6. Notification Controller creates AdapterActionRequest. +7. email-connect sends email. +8. email-connect emits provider accepted event. +9. Evidence Normalizer maps it to notification.attempt.accepted_by_provider. +10. Evidence Ledger records normalized event. +11. Later, portal-connect emits authenticated download event. +12. Evidence Ledger records delivery evidence. +13. Evidence Ceiling Evaluator confirms portal event can satisfy delivery/access. +14. Result Controller marks participant complete. +15. Coordination Controller updates case progress. +``` + +## 18.2 Multi-Channel Fallback Flow + +```text id="9ezwd1" +1. Email MX acceptance is recorded. +2. No portal access is observed after threshold. +3. Policy Engine checks email evidence ceiling. +4. Email evidence is insufficient for awareness requirement. +5. Capability Resolver selects SMS fallback. +6. sms-connect sends SMS reminder. +7. SMS delivery receipt arrives. +8. Participant remains unresolved if scenario requires payload access. +9. Portal access later completes participant result. +``` + +## 18.3 Hybrid-Mail Fallback Flow + +```text id="o1xstw" +1. Email and SMS fail or remain unresolved. +2. Policy Engine selects hybridmail-connect. +3. hybridmail-connect submits physical letter. +4. Provider validates document. +5. Provider reports postal handover. +6. Evidence Ceiling Evaluator marks strong dispatch evidence. +7. Result policy determines whether dispatch is sufficient. +8. If delivery confirmation is required, case remains pending until stronger postal evidence or fallback action. +9. If return mail arrives late, Late Event Processor triggers reassessment. +``` + +## 18.4 Payment Collection Flow + +```text id="jc5ryh" +1. Client creates payment collection case. +2. Payload Controller registers invoices. +3. payment-connect creates payment requests. +4. email-connect sends payment notifications. +5. payment-connect emits payment_started and payment_settled events. +6. Evidence Ledger records events. +7. Result Controller marks paid participants complete. +8. Policy Engine triggers reminders for unpaid participants. +``` + +## 18.5 Contract Signing Flow + +```text id="b1h62u" +1. Client creates contract closure case. +2. Payload Controller registers contract draft. +3. Access Controller grants contract access. +4. signature-connect creates signature flow. +5. email-connect sends notification. +6. signature-connect emits viewed, signed, declined, expired events. +7. Identity & Authority Controller evaluates signer authority. +8. Result Controller closes participant on valid signature. +9. Coordination Controller closes case when required signatures are complete. +``` + +## 19. Reliability Requirements + +The architecture shall support: + +* idempotent action requests +* duplicate event detection +* late event handling +* out-of-order event handling +* retryable and non-retryable errors +* provider outages +* adapter degradation +* correlation across systems +* partial failure +* conflicting evidence +* manual correction or override +* raw event preservation +* state reassessment after late evidence + +## 20. Security Requirements + +The architecture shall support: + +* adapter credential isolation +* least-privilege adapter access +* secure webhook verification +* event authenticity validation where possible +* sensitive metadata minimization +* secure access grant handling +* audit logs for policy-driven and manual actions +* separation between payload storage and coordination metadata +* strict duplicate side-effect prevention for irreversible actions + +## 21. Privacy Requirements + +The engine should avoid storing unnecessary personal data. + +Recommended practices: + +* store participant references instead of full profiles where possible +* store endpoint references instead of raw endpoint values where possible +* store payload references instead of payload content +* support retention policies +* support evidence minimization +* support raw-event redaction +* separate audit requirements from operational tracking requirements +* define adapter-specific privacy profiles + +## 22. Observability Requirements + +The system should expose metrics for: + +* active cases +* completed cases +* failed cases +* expired cases +* unresolved participants +* generated next actions +* adapter action success/failure +* adapter event ingestion latency +* evidence event volume +* late event volume +* policy evaluation results +* channel-level failure rates +* endpoint quality degradation +* evidence-ceiling insufficiency counts +* adapter health changes +* case-level progress +* deadline risk + +## 23. MVP Architecture + +The MVP should implement a minimal but coherent version of the architecture. + +## 23.1 MVP Components + +Required: + +* Coordination Controller +* Result Controller +* Participant Controller +* Payload Controller +* Access Controller +* Notification Controller +* Delivery Controller +* Interaction Controller +* Evidence Ledger +* Policy Engine +* Adapter Registry +* Capability Resolver +* Evidence Normalizer +* Evidence Ceiling Evaluator +* Adapter Contract +* Simulated Adapter + +Recommended: + +* email-connect reference adapter +* portal/action-surface simulated adapter +* webhook event ingestion +* basic endpoint quality store +* basic late event processor + +## 23.2 MVP Scenario + +The recommended MVP scenario is **Digital Payload Notification and Access**. + +Flow: + +```text id="v7i9l1" +create case +add participants +register payload +grant access +send notification +record provider acceptance +record ambiguous/no signal cases +record authenticated payload access +derive participant states +generate reminders +close completed participants +close case when result predicate is satisfied +``` + +## 23.3 MVP Acceptance Criteria + +The MVP shall demonstrate: + +1. Creation of a multi-participant coordination case. +2. Registration of one outbound payload. +3. Definition of an intended result. +4. Dispatch of notification actions through an adapter. +5. Recording of normalized evidence events. +6. Participant-level state derivation. +7. Case-level progress assessment. +8. Ambiguous evidence handling. +9. Policy-generated reminder or escalation. +10. Closure based on sufficient delivery or interaction evidence. +11. Traceability from final state to evidence events. +12. Evidence ceiling preventing overclaiming. +13. Capability-based adapter selection. +14. Late event ingestion and reassessment at least in simulated form. + +## 24. Open Technical Questions + +1. Should policy rules be expressed as JSON/YAML, a DSL, embedded code, or an external rule engine? +2. Should the evidence ledger be strictly append-only from the first implementation? +3. Should adapter events be pushed, pulled, or both? +4. Should adapters call the engine directly or publish to an event bus? +5. How should raw event payloads be stored, redacted, and retained? +6. Should the engine own scheduling, or should it emit next actions to an external scheduler? +7. Should result predicates be evaluated synchronously on every event or asynchronously by a worker? +8. What is the minimal adapter contract version needed for first implementation? +9. Which adapter should become the first real reference implementation? +10. How should manual override events be represented and audited? +11. How should evidence ceilings interact with multi-adapter evidence fusion? +12. Should endpoint quality be global, tenant-scoped, case-scoped, or all three? +13. When should late events reopen a case versus merely annotate it? +14. How should golden tests be executed across separate adapter repositories? + +## 25. Non-Goals + +The architecture does not attempt to make `coordination-engine`: + +* a general-purpose BPMN engine +* an email provider +* an SMS provider +* an RSS feed platform +* an XMPP server +* a hybrid-mail provider +* a payment provider +* a signature provider +* a document store +* a CRM +* an ERP +* a legal proof system by default +* a complete UI application + +The engine coordinates across such systems through adapters. + +## 26. Strategic Implementation Guidance + +The recommended implementation path is: + +1. Define core schemas. +2. Implement evidence ledger and case state derivation. +3. Implement adapter registry. +4. Implement capability resolver. +5. Implement evidence normalizer. +6. Implement evidence ceiling evaluator. +7. Implement adapter contract v1.1. +8. Implement simulated adapter. +9. Implement minimal notification-and-access scenario. +10. Create `email-connect` as reference notification adapter. +11. Add portal/action-surface adapter or simulation. +12. Validate policy-driven follow-up. +13. Add late-event simulation. +14. Extend toward SMS, RSS, XMPP, hybrid mail, payment, signature, and document adapters. + +## 27. Summary + +`coordination-engine` provides a digital-first coordination runtime. It manages goal-directed coordination cases by combining participants, payloads, action surfaces, access, notifications, deliveries, interactions, evidence, and policies. + +Adapters are the bridge between the engine and the outside world, but they are not mere technical connectors. They are semantic boundary components. They provide actions, observe signals, preserve native evidence, map provider-specific events conservatively, declare evidence ceilings, expose endpoint quality, and report limitations. + +The core architectural idea is: + +> Adapters translate external capabilities and observations into coordination actions and evidence events. coordination-engine uses those events, constrained by adapter semantics and scenario policies, to evaluate intended results and control follow-up under uncertainty. + +The core safety rule is: + +> The runtime must underclaim rather than overclaim. Evidence may be combined across adapters to reach stronger conclusions, but no single adapter event may be interpreted beyond its declared evidence ceiling. + diff --git a/spec/SmsAdapterSpecification.md b/spec/SmsAdapterSpecification.md new file mode 100644 index 0000000..de6fb4c --- /dev/null +++ b/spec/SmsAdapterSpecification.md @@ -0,0 +1,1699 @@ +I used current provider documentation and industry guidance to ground the SMS model. Twilio’s outbound status model includes states such as queued/sending/sent/delivered/undelivered/failed, and notes that `sent` may remain without final delivery status in some cases. ([help.twilio.com][1]) Vonage’s SMS DLRs include `accepted`, `delivered`, `buffered`, `expired`, `failed`, `rejected`, and `unknown`; Vonage also notes DLRs are returned when received from downstream carriers. ([developer.vonage.com][2]) Sinch’s SMS delivery reports use statuses such as queued, dispatched, delivered and error codes, while Bird/MessageBird distinguish delivered from not delivered and provide reason codes such as unknown subscriber. ([developers.sinch.com][3]) SMS content length, encoding, and concatenation matter because GSM-7, UCS-2, and multi-segment messages affect cost, delivery behavior, and diagnostics. ([Twilio][4]) For compliance-sensitive SMS, opt-in/opt-out, STOP/HELP handling, and sender registration are first-class channel concerns; Twilio documents Advanced Opt-Out and A2P 10DLC registration, while CTIA guidance emphasizes documenting consent and respecting opt-out. ([Twilio][5]) + +# SmsAdapterSpecification.md + +## 1. Document Status + +**Document:** SmsAdapterSpecification.md +**Project:** sms-connect +**Target Integration:** coordination-engine +**Adapter Contract:** AdapterInterfaceSpecification.md v1.0 +**Status:** Draft v1.0 +**Primary Scope:** SMS protocol and provider integration as a coordination-engine adapter + +## 2. Purpose + +This document specifies how `sms-connect` models SMS as a communication protocol and how it integrates with `coordination-engine`. + +`sms-connect` is a channel adapter for short-message communication. It provides SMS-specific sending, delivery receipt ingestion, status normalization, phone endpoint quality handling, opt-out/suppression handling, inbound reply processing, evidence classification, and provider abstraction. + +SMS is often stronger than email for endpoint-level delivery evidence because carriers and providers may return delivery receipts indicating that a message reached the receiving network or handset. However, SMS still does not normally prove human awareness, comprehension, identity-bound interaction, payload access, or final coordination success. + +The key design objective is to make SMS useful as a notification, reminder, escalation, and lightweight interaction channel without overclaiming what SMS can prove. + +## 3. Core Principle + +SMS is a medium-strength notification channel with better endpoint-delivery evidence than email in many cases, but it is still not a reliable proof channel for human awareness or result completion. + +The adapter MUST distinguish: + +```text +provider acceptance +carrier or downstream network acceptance +delivery receipt +temporary delivery uncertainty +permanent delivery failure +recipient reply +opt-out or suppression +link interaction +identity-bound interaction from another system +coordination result evidence +``` + +In the coordination model, SMS delivery receipts can support stronger notification evidence than email MX acceptance, but SMS events alone usually remain insufficient for high-assurance scenarios unless the intended result explicitly accepts SMS delivery or SMS reply as sufficient. + +## 4. Architectural Role + +### 4.1 Standalone Role + +As a standalone component, `sms-connect` provides: + +* provider-neutral SMS sending +* transactional SMS dispatch +* provider and carrier delivery receipt ingestion +* message status normalization +* phone number endpoint diagnostics +* SMS segmentation and encoding diagnostics +* inbound SMS reply handling +* opt-in, opt-out, HELP, START, and STOP keyword handling +* suppression management +* sender identity and campaign metadata tracking +* SMS message timelines +* delivery failure classification +* channel health and compliance diagnostics +* normalized SMS evidence stream + +### 4.2 coordination-engine Adapter Role + +As a `coordination-engine` adapter, `sms-connect` provides: + +#### Actions + +* `notification.send` +* `notification.send_reminder` +* `notification.schedule` +* `notification.cancel` where technically possible before dispatch +* `recipient.suppress` +* `recipient.unsuppress` +* `recipient.opt_in_record` +* `recipient.opt_out_record` +* `notification.register_tracking_context` + +#### Signals + +* `notification.attempt.accepted_by_adapter` +* `notification.attempt.rejected_by_adapter` +* `notification.attempt.accepted_by_provider` +* `notification.attempt.rejected_by_provider` +* `notification.attempt.queued` +* `notification.attempt.scheduled` +* `notification.attempt.delayed` +* `notification.endpoint.accepted` +* `notification.endpoint.deferred` +* `notification.endpoint.rejected_temporary` +* `notification.endpoint.rejected_permanent` +* `notification.endpoint.unreachable` +* `notification.endpoint.unknown` +* `notification.channel.suppression_added` +* `notification.channel.suppression_removed` +* `notification.channel.complaint_received` +* `notification.channel.unsubscribe_received` +* `notification.channel.reputation_warning` +* `interaction.reply_received` +* `interaction.acknowledgement_recorded` +* `interaction.unverified_actor_interaction` +* `interaction.authenticated_actor_interaction` only when paired with trusted identity evidence +* `system.provider.degraded` +* `system.provider.unavailable` + +## 5. Relationship to coordination-engine + +`sms-connect` does not own: + +* `CoordinationCase` +* intended result evaluation +* participant-level success +* case-level success +* legal notification interpretation +* portal access +* payload retrieval +* payment settlement +* signature completion +* multi-channel escalation policy +* final closure decisions + +`sms-connect` owns: + +* SMS send requests +* SMS provider abstraction +* provider and carrier identifiers +* SMS delivery receipt ingestion +* SMS status classification +* SMS-native message timeline +* phone endpoint diagnostics +* SMS reply classification +* opt-out and suppression handling +* SMS evidence mapping +* SMS-specific uncertainty classification + +The boundary rule is: + +> sms-connect reports what happened in the SMS channel and what that may indicate. coordination-engine decides what that means for the coordination case. + +## 6. SMS as Notification, Lightweight Payload, and Response Channel + +Within `coordination-engine`, SMS is primarily a **notification and reminder channel**. + +SMS may also carry lightweight inline payloads or action prompts, such as: + +* one-time codes +* short status alerts +* short links +* payment links +* confirmation prompts +* reply keywords +* appointment reminders +* urgent escalation notices + +However, for most coordination scenarios, SMS should point participants toward an action surface such as: + +* portal page +* mobile app screen +* payment page +* signature flow +* upload form +* approval screen +* support flow + +The actual payload access or result-relevant action is usually observed by another adapter. + +Example: + +```text +sms-connect: + notification.endpoint.accepted + interaction.reply_received + +portal-connect: + identity.actor_authenticated + delivery.payload.downloaded + +coordination-engine: + participant result satisfied +``` + +SMS delivery to handset or network is stronger than provider acceptance but still does not prove that the intended human read, understood, or acted on the message. + +## 7. SMS Message Lifecycle Model + +`sms-connect` models an SMS notification as a lifecycle with observable phases. + +```text +message.created +message.rendered +message.render_failed +message.segmentation_analyzed +message.consent_checked +message.suppression_checked +message.send_requested +message.accepted_by_adapter +message.rejected_by_adapter +message.accepted_by_provider +message.rejected_by_provider +message.queued +message.scheduled +message.dispatched +message.accepted_by_carrier +message.buffered +message.deferred +message.delivered +message.undelivered +message.failed +message.rejected +message.expired +message.unknown +message.inbound_reply_received +message.opt_out_received +message.opt_in_received +message.help_received +message.link_clicked +message.suppression_added +message.channel_warning +``` + +The lifecycle is not strictly linear. Events may arrive late, out of order, duplicated, partially aggregated, or missing. + +## 8. SMS Message vs Attempt vs Segment vs Recipient + +The adapter MUST distinguish at least four layers. + +### 8.1 SmsMessage + +The logical SMS message created by a client or coordination case. + +```yaml +SmsMessage: + sms_message_id: string + coordination_case_id: string? + participant_id: string? + purpose: string? + message_body_ref: string? + template_ref: string? + tracking_context: TrackingContext + created_at: timestamp +``` + +### 8.2 SmsAttempt + +One send attempt through one provider/configuration. + +```yaml +SmsAttempt: + sms_attempt_id: string + sms_message_id: string + provider_name: string + provider_account_ref: string? + sender_endpoint: SmsEndpoint + recipient_endpoint: SmsEndpoint + provider_message_id: string? + adapter_operation_id: string? + sender_identity_ref: string? + campaign_ref: string? + state: SmsAttemptState + created_at: timestamp + updated_at: timestamp +``` + +### 8.3 SmsSegment + +A logical SMS may be split into multiple billable/deliverable segments. + +```yaml +SmsSegment: + sms_segment_id: string + sms_attempt_id: string + segment_index: integer + segment_count: integer + encoding: gsm7 | ucs2 | binary | unknown + provider_segment_id: string? + carrier_segment_id: string? + state: SmsSegmentState + delivered_at: timestamp? + failed_at: timestamp? +``` + +Segmentation matters for cost, deliverability diagnostics, and partial-failure interpretation. For coordination evidence, a multi-segment message should normally be considered delivered only when the provider or carrier reports the message as delivered or enough segment-level evidence exists according to provider semantics. + +### 8.4 SmsEndpoint + +The target or sender endpoint. + +```yaml +SmsEndpoint: + endpoint_id: string? + endpoint_type: phone_number | short_code | long_code | toll_free | alphanumeric_sender_id | sender_pool | unknown + phone_number_e164: string? + sender_id: string? + display_name: string? + country_code: string? + verification_state: unknown | syntax_validated | verified | suspected_invalid | invalid + consent_state: unknown | opted_in | opted_out | consent_not_required | consent_required | disputed + suppression_state: active | suppressed | opt_out_suppressed | bounce_suppressed | provider_suppressed | manual_suppressed | unknown + metadata: object? +``` + +Per-recipient modeling is required. The preferred coordination mode is one logical notification attempt per participant endpoint. + +## 9. SMS Attempt States + +The adapter SHOULD support these attempt states: + +```text +created +rendered +render_failed +consent_blocked +suppressed +send_requested +accepted_by_adapter +rejected_by_adapter +accepted_by_provider +rejected_by_provider +queued +scheduled +dispatched +accepted_by_carrier +buffered +deferred +delivered +undelivered +failed +rejected +expired +unknown +reply_received +opt_out_received +opt_in_received +help_received +channel_degraded +``` + +These states are SMS-native. They are not coordination result states. + +## 10. SMS Evidence Assessment + +`sms-connect` should provide an SMS-native assessment separate from coordination-engine state. + +```yaml +SmsEvidenceAssessment: + sms_message_id: string + participant_id: string? + category: success | fail | undef + subclass: string + confidence: low | medium | high + strongest_signal: string? + evidence_summary: + - string + recommended_coordination_interpretation: string? +``` + +The assessment categories are adapter-level hints. + +### 10.1 SMS Adapter Success + +SMS-level `success` means the SMS channel produced strong SMS-channel evidence. + +Possible subclasses: + +```text +success.provider_accepted +success.carrier_accepted +success.delivered +success.reply_received +success.acknowledgement_reply_received +success.link_clicked +``` + +Important: These are not automatically coordination success. + +`success.delivered` usually means a provider or carrier reports delivery to the handset or receiving network. It is stronger than email server acceptance, but it still does not prove human awareness unless scenario policy accepts delivery receipt as sufficient. + +### 10.2 SMS Adapter Fail + +SMS-level `fail` indicates strong evidence that the SMS channel failed or should not be used. + +Subclasses: + +```text +fail.missing_number +fail.invalid_number_format +fail.not_mobile_number +fail.unknown_subscriber +fail.unreachable_subscriber +fail.unavailable_subscriber +fail.ported_number_issue +fail.blocked_by_recipient +fail.opted_out +fail.consent_missing +fail.suppressed +fail.provider_rejected +fail.carrier_rejected +fail.filtered_by_carrier +fail.policy_rejected +fail.content_rejected +fail.sender_not_registered +fail.sender_not_allowed +fail.route_unavailable +fail.expired +fail.delivery_failed +fail.message_too_long_or_invalid +fail.encoding_unsupported +fail.account_or_quota_failure +``` + +### 10.3 SMS Adapter Undef + +SMS-level `undef` is used when the adapter cannot determine final delivery or human awareness. + +Subclasses: + +```text +undef.pending +undef.provider_accepted_only +undef.queued +undef.dispatched +undef.carrier_accepted_only +undef.buffered +undef.deferred +undef.no_dlr +undef.dlr_unknown +undef.delivery_status_unknown +undef.delivery_receipt_missing +undef.delivered_but_awareness_unproven +undef.link_clicked_identity_uncertain +undef.reply_identity_uncertain +undef.conflicting_evidence +undef.channel_degraded +undef.expired_unknown +``` + +The `undef` category must not be empty because SMS delivery reporting is provider-, route-, country-, carrier-, and device-dependent. + +## 11. Detailed SMS Scenario Classification + +### 11.1 Pre-Send Scenarios + +| Scenario | SMS assessment | Normalized event | Notes | +| -------------------------------------- | --------------------------------------------------------------- | ------------------------------------------ | ---------------------------------------- | +| Missing number | `fail.missing_number` | `notification.attempt.rejected_by_adapter` | No send possible | +| Invalid phone number syntax | `fail.invalid_number_format` | `notification.attempt.rejected_by_adapter` | Strong local failure | +| Number not suitable for SMS | `fail.not_mobile_number` | `notification.attempt.rejected_by_adapter` | Landline or unsupported endpoint | +| Missing consent where required | `fail.consent_missing` | `notification.attempt.rejected_by_adapter` | Policy/compliance block | +| Recipient opted out | `fail.opted_out` | `notification.channel.suppression_added` | Channel blocked | +| Suppression hit | `fail.suppressed` | `notification.channel.suppression_added` | Channel blocked | +| Template rendering failure | `fail.message_too_long_or_invalid` or `fail.provider_rejected` | `notification.attempt.rejected_by_adapter` | Notification unusable | +| Message contains invalid link | `fail.message_too_long_or_invalid` or scenario-specific failure | `notification.attempt.rejected_by_adapter` | Especially relevant for portal links | +| Sender/campaign missing | `fail.sender_not_registered` or `fail.sender_not_allowed` | `notification.attempt.rejected_by_adapter` | Common in regulated routes | +| Provider unavailable before acceptance | `undef.pending` or `fail.provider_rejected` | action error | Depends on retryability | +| Submission timeout | `undef.pending` | action result unknown | Could have been accepted despite timeout | + +### 11.2 Provider-Side Scenarios + +| Scenario | SMS assessment | Normalized event | Notes | +| ------------------------------ | ------------------------------- | ------------------------------------------------------- | ------------------------------ | +| Adapter accepted request | `undef.pending` | `notification.attempt.accepted_by_adapter` | Adapter accepted work | +| Provider accepted message | `undef.provider_accepted_only` | `notification.attempt.accepted_by_provider` | Sending began | +| Provider rejected message | `fail.provider_rejected` | `notification.attempt.rejected_by_provider` | Strong attempt failure | +| Provider queued message | `undef.queued` | `notification.attempt.queued` | Pending | +| Provider scheduled message | `undef.pending` | `notification.attempt.scheduled` | Not yet sent | +| Provider dispatched message | `undef.dispatched` | `notification.attempt.accepted_by_provider` or metadata | Provider handed off downstream | +| Provider delayed message | `undef.deferred` | `notification.attempt.delayed` | Pending/degraded | +| Provider route unavailable | `fail.route_unavailable` | `notification.attempt.rejected_by_provider` | Strong channel/route failure | +| Provider quota/account failure | `fail.account_or_quota_failure` | `notification.attempt.rejected_by_provider` | Operational failure | +| Provider suppression | `fail.suppressed` | `notification.channel.suppression_added` | Channel blocked | + +### 11.3 Carrier and Downstream Network Scenarios + +| Scenario | SMS assessment | Normalized event | Notes | +| ---------------------------- | ----------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------- | +| Carrier accepted | `undef.carrier_accepted_only` | `notification.endpoint.accepted` | Stronger than provider-only, still not handset proof | +| Buffered | `undef.buffered` | `notification.endpoint.deferred` | Held for later delivery | +| Deferred | `undef.deferred` | `notification.endpoint.deferred` | Retry or wait | +| Delivered | `success.delivered` | `notification.endpoint.accepted` with delivery metadata | Strong endpoint evidence, not human awareness | +| Undelivered | `fail.delivery_failed` | `notification.endpoint.rejected_permanent` or temporary | Use reason code | +| Failed | `fail.delivery_failed` | `notification.endpoint.rejected_permanent` | Strong negative evidence if final | +| Rejected | `fail.carrier_rejected` | `notification.endpoint.rejected_permanent` | Carrier refused delivery | +| Expired | `fail.expired` or `undef.expired_unknown` | `notification.endpoint.unknown` or rejected temporary/permanent | Depends on provider semantics | +| Unknown | `undef.dlr_unknown` | `notification.endpoint.unknown` | No useful final status | +| No DLR received | `undef.no_dlr` | none or timeout event | Common on some routes | +| Delivery receipt late | update current assessment | corresponding event | Must be accepted if relevant | +| Conflicting delivery reports | `undef.conflicting_evidence` | multiple events | Preserve all evidence | + +### 11.4 Device and Recipient Availability Scenarios + +| Scenario | SMS assessment | Notes | +| -------------------------------- | ------------------------------------------------- | ---------------------------------------------- | +| Handset off or unreachable | `undef.deferred` or `fail.unreachable_subscriber` | Depending on final DLR | +| Subscriber unavailable | `undef.deferred` or `fail.unavailable_subscriber` | Carrier-specific | +| Unknown subscriber | `fail.unknown_subscriber` | Remove or verify number | +| Roaming issue | `undef.deferred` or `fail.route_unavailable` | Route/country dependent | +| Blocked by recipient | `fail.blocked_by_recipient` | If provider/carrier reports it | +| Carrier filtering | `fail.filtered_by_carrier` | Often content/campaign/sender issue | +| Device received but user ignored | `undef.delivered_but_awareness_unproven` | Delivery does not prove reading | +| SIM swap/number reassigned | `undef.identity_uncertain` if suspected | Requires external identity/number intelligence | +| Shared phone | `undef.identity_uncertain` | Intended participant uncertain | +| Corporate device management | `undef.delivered_but_awareness_unproven` | User awareness uncertain | + +### 11.5 Link Interaction Scenarios + +SMS itself does not provide open tracking. Link interaction must be observed through tracked links or action surfaces. + +| Scenario | SMS assessment | Normalized event | Notes | +| -------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------ | -------------------------------------- | +| No link click | `undef.no_signal` | none | Not evidence of failure | +| Link clicked | `success.link_clicked` or `undef.link_clicked_identity_uncertain` | `interaction.unverified_actor_interaction` | Identity uncertain | +| Link clicked then authenticated portal login | SMS event remains weak; portal event is strong | Portal adapter provides identity evidence | | +| Link clicked then payload downloaded | SMS contributed path evidence; delivery evidence is decisive | Portal/delivery adapter closes result | | +| Link scanner or preview fetch | `undef.link_clicked_identity_uncertain` or scanner-like if detected | `interaction.scanner_or_bot_interaction` | Less common than email but possible | +| Expired link clicked | `undef.link_clicked_identity_uncertain` | interaction plus access failure | Awareness possible, action path failed | +| Forwarded SMS link clicked | `undef.identity_uncertain` | unverified interaction | Intended actor unknown | + +### 11.6 Inbound Reply Scenarios + +SMS can be a useful lightweight response channel. + +| Scenario | SMS assessment | Normalized event | Notes | +| ----------------------------------------------- | ---------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------- | +| Any reply received | `success.reply_received` | `interaction.reply_received` | Strong awareness signal, identity may still need validation | +| YES/OK/ACK reply | `success.acknowledgement_reply_received` | `interaction.acknowledgement_recorded` if policy accepts | Can be result evidence in low/medium assurance cases | +| STOP / unsubscribe | `fail.opted_out` | `notification.channel.unsubscribe_received` + suppression | Future channel constraint | +| START / opt-in | active/unknown | `notification.channel.suppression_removed` or opt-in metadata | Channel may become usable | +| HELP | neutral | `interaction.reply_received` | Support/compliance event | +| Free-text question | `success.reply_received` | `interaction.reply_received` | May trigger support workflow | +| Reply from reassigned/shared number | `undef.reply_identity_uncertain` | `interaction.reply_received` | Identity risk | +| Reply from unsupported alphanumeric sender path | no inbound possible | capability limitation | Must be declared by adapter | + +A reply is often stronger awareness evidence than a delivery receipt, but it still may not prove that the intended participant acted unless phone-number ownership and context are sufficient for the scenario. + +### 11.7 Opt-Out, Consent, and Suppression Scenarios + +| Scenario | SMS assessment | Normalized event | Notes | +| -------------------- | ----------------------- | --------------------------------------------------------- | --------------------------- | +| Consent recorded | channel usable | adapter metadata or evidence | May be required before send | +| Consent missing | `fail.consent_missing` | `notification.attempt.rejected_by_adapter` | Local policy block | +| STOP received | `fail.opted_out` | `notification.channel.unsubscribe_received` + suppression | Channel blocked | +| START received | channel may be restored | `notification.channel.suppression_removed` | Provider/country dependent | +| HELP received | neutral/support event | `interaction.reply_received` | May trigger help response | +| Manual suppression | `fail.suppressed` | `notification.channel.suppression_added` | Operator action | +| Provider suppression | `fail.suppressed` | `notification.channel.suppression_added` | Provider-managed | +| Suppression removed | active/unknown | `notification.channel.suppression_removed` | Channel may become usable | + +For non-marketing or legally required notifications, opt-out semantics must be scenario-specific. `sms-connect` records the event; `coordination-engine` decides whether SMS remains permissible, whether fallback is needed, or whether manual handling is required. + +## 12. Adapter-to-Coordination Mapping + +### 12.1 Core Mapping Table + +| SMS-native event | SMS assessment | coordination-engine event | Coordination interpretation | +| -------------------- | ----------------------------------------- | -------------------------------------------------------- | ----------------------------------------- | +| message created | `undef.pending` | `notification.attempt.created` | Attempt exists | +| render failed | `fail.message_too_long_or_invalid` | `notification.attempt.rejected_by_adapter` | Notification failed before send | +| consent block | `fail.consent_missing` | `notification.attempt.rejected_by_adapter` | Local policy block | +| suppression hit | `fail.suppressed` | `notification.channel.suppression_added` | Channel blocked | +| adapter accepted | `undef.pending` | `notification.attempt.accepted_by_adapter` | Work accepted | +| adapter rejected | adapter-specific fail | `notification.attempt.rejected_by_adapter` | Attempt failed | +| provider accepted | `undef.provider_accepted_only` | `notification.attempt.accepted_by_provider` | Weak send evidence | +| provider rejected | `fail.provider_rejected` | `notification.attempt.rejected_by_provider` | Strong attempt failure | +| provider queued | `undef.queued` | `notification.attempt.queued` | Pending | +| dispatched | `undef.dispatched` | `notification.attempt.accepted_by_provider` + metadata | Sent downstream | +| carrier accepted | `undef.carrier_accepted_only` | `notification.endpoint.accepted` | Medium endpoint evidence | +| buffered | `undef.buffered` | `notification.endpoint.deferred` | Pending | +| delivered | `success.delivered` | `notification.endpoint.accepted` with delivered metadata | Strong endpoint evidence, not human proof | +| undelivered | `fail.delivery_failed` | `notification.endpoint.rejected_permanent` or temporary | Use reason code | +| failed | `fail.delivery_failed` | `notification.endpoint.rejected_permanent` | Strong final failure | +| rejected | `fail.carrier_rejected` | `notification.endpoint.rejected_permanent` | Strong final failure | +| expired | `fail.expired` or `undef.expired_unknown` | `notification.endpoint.unknown` or rejected | Depends on reason | +| unknown | `undef.dlr_unknown` | `notification.endpoint.unknown` | Unclear outcome | +| inbound reply | `success.reply_received` | `interaction.reply_received` | Strong awareness evidence | +| ACK reply | `success.acknowledgement_reply_received` | `interaction.acknowledgement_recorded` | Result evidence if policy accepts | +| STOP | `fail.opted_out` | `notification.channel.unsubscribe_received` | Channel constraint | +| START | active/unknown | `notification.channel.suppression_removed` | Channel may be usable | +| tracked link clicked | `undef.link_clicked_identity_uncertain` | `interaction.unverified_actor_interaction` | Identity uncertain | + +### 12.2 Coordination Undef Subclasses + +`coordination-engine` may derive these participant uncertainty classes from SMS evidence: + +```text +undef.pending +undef.technical_acceptance_only +undef.endpoint_acceptance_only +undef.delivered_but_awareness_unproven +undef.no_signal +undef.weak_positive +undef.identity_uncertain +undef.channel_suspicious +undef.conflicting_evidence +undef.delivery_pending +undef.escalation_required +``` + +SMS evidence commonly produces: + +```text +undef.pending +undef.endpoint_acceptance_only +undef.delivered_but_awareness_unproven +undef.identity_uncertain +undef.conflicting_evidence +``` + +## 13. Evidence Grading Rules + +### 13.1 Provider Acceptance + +```yaml +event_type: notification.attempt.accepted_by_provider +evidence_grade: + strength: weak + actor_certainty: none + authority_certainty: none + payload_certainty: low + interaction_certainty: none + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: none +notes: + - Provider accepted message for processing. + - Does not prove carrier acceptance or handset delivery. +``` + +### 13.2 Carrier Acceptance + +```yaml +event_type: notification.endpoint.accepted +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: low + interaction_certainty: none + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: low +notes: + - Downstream carrier or network accepted the message. + - Does not prove recipient read or understood the message. +``` + +### 13.3 Delivered DLR + +```yaml +event_type: notification.endpoint.accepted +evidence_grade: + strength: strong + actor_certainty: none + authority_certainty: none + payload_certainty: medium + interaction_certainty: low + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Delivery receipt indicates successful delivery according to provider/carrier semantics. + - Does not prove human awareness or intended-recipient identity. +``` + +### 13.4 Undelivered / Failed + +```yaml +event_type: notification.endpoint.rejected_permanent +evidence_grade: + strength: negative + actor_certainty: none + authority_certainty: none + payload_certainty: none + interaction_certainty: none + timing_certainty: medium + channel_certainty: high + non_repudiation_strength: low +notes: + - Strong evidence that the SMS channel failed for this attempt. + - Failure reason should determine retry, fallback, or suppression. +``` + +### 13.5 Buffered / Deferred + +```yaml +event_type: notification.endpoint.deferred +evidence_grade: + strength: ambiguous + actor_certainty: none + authority_certainty: none + payload_certainty: none + interaction_certainty: none + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: none +notes: + - Message is pending or held for later delivery. + - Outcome remains uncertain until final DLR, timeout, or expiry. +``` + +### 13.6 Unknown / No DLR + +```yaml +event_type: notification.endpoint.unknown +evidence_grade: + strength: ambiguous + actor_certainty: none + authority_certainty: none + payload_certainty: none + interaction_certainty: none + timing_certainty: low + channel_certainty: low + non_repudiation_strength: none +notes: + - No useful delivery status is available. + - Policy should decide wait, retry, alternate channel, or manual review. +``` + +### 13.7 Inbound Reply + +```yaml +event_type: interaction.reply_received +evidence_grade: + strength: strong + actor_certainty: medium + authority_certainty: low + payload_certainty: medium + interaction_certainty: high + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Reply indicates interaction from the phone endpoint. + - Identity and authority may still require validation. +``` + +### 13.8 Acknowledgement Reply + +```yaml +event_type: interaction.acknowledgement_recorded +evidence_grade: + strength: strong + actor_certainty: medium + authority_certainty: low + payload_certainty: medium + interaction_certainty: high + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Recipient endpoint replied with an acknowledgement keyword. + - Whether this satisfies the intended result is scenario-policy dependent. +``` + +### 13.9 Opt-Out + +```yaml +event_type: notification.channel.unsubscribe_received +evidence_grade: + strength: negative + actor_certainty: medium + authority_certainty: low + payload_certainty: none + interaction_certainty: high + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Phone endpoint opted out or requested no further messages. + - Future use of the channel may be legally, contractually, or operationally constrained. +``` + +### 13.10 Link Click + +```yaml +event_type: interaction.unverified_actor_interaction +evidence_grade: + strength: medium + actor_certainty: low + authority_certainty: none + payload_certainty: medium + interaction_certainty: medium + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: none +notes: + - Link was clicked, but actor identity is not proven. + - Stronger evidence should come from the action surface or identity adapter. +``` + +## 14. Message Timeline API + +`sms-connect` SHOULD expose a message timeline suitable for standalone diagnostics and coordination audit. + +```yaml +SmsMessageTimeline: + sms_message_id: string + sms_attempts: + - sms_attempt_id: string + provider_name: string + provider_message_id: string? + events: + - SmsTimelineEvent + segments: + - SmsSegment + current_assessment: SmsEvidenceAssessment +``` + +```yaml +SmsTimelineEvent: + event_id: string + event_type: string + occurred_at: timestamp + source: adapter | provider | carrier | inbound_sms | tracking | operator + native_event_type: string? + normalized_event_type: string? + summary: string + evidence_grade: EvidenceGrade + raw_event_ref: string? +``` + +## 15. Adapter Descriptor + +`sms-connect` MUST expose an `AdapterDescriptor` compatible with AdapterInterfaceSpecification.md v1.0. + +```yaml +adapter_id: sms-connect.default +adapter_name: sms-connect +adapter_version: 1.0.0 +adapter_contract_version: 1.0 +adapter_types: + - notification + - communication + - interaction +provider_family: sms +provider_name: configurable +deployment_mode: external +supported_channels: + - sms +supported_endpoint_types: + - phone_number + - short_code + - long_code + - toll_free + - alphanumeric_sender_id +supported_actions: + - action_type: notification.send + mode: async + idempotency_required: true + - action_type: notification.send_reminder + mode: async + idempotency_required: true + - action_type: notification.schedule + mode: scheduled + idempotency_required: true + - action_type: notification.register_tracking_context + mode: sync + idempotency_required: true + - action_type: recipient.suppress + mode: sync + idempotency_required: true + - action_type: recipient.unsuppress + mode: sync + idempotency_required: true +emitted_event_types: + - notification.attempt.created + - notification.attempt.accepted_by_adapter + - notification.attempt.rejected_by_adapter + - notification.attempt.accepted_by_provider + - notification.attempt.rejected_by_provider + - notification.attempt.queued + - notification.attempt.scheduled + - notification.attempt.delayed + - notification.endpoint.accepted + - notification.endpoint.deferred + - notification.endpoint.rejected_temporary + - notification.endpoint.rejected_permanent + - notification.endpoint.unreachable + - notification.endpoint.unknown + - notification.channel.unsubscribe_received + - notification.channel.suppression_added + - notification.channel.suppression_removed + - notification.channel.reputation_warning + - interaction.reply_received + - interaction.acknowledgement_recorded + - interaction.unverified_actor_interaction + - system.provider.degraded + - system.provider.unavailable +evidence_profile: + strongest_evidence_level: medium_to_strong + can_prove_human_awareness: false + can_prove_payload_delivery: false + can_prove_identity: false +identity_profile: + identity_strength: low_to_medium + authority_strength: none_to_low +limitations: + - SMS delivery receipts do not prove that the intended human read the message. + - Some routes do not provide reliable final delivery receipts. + - Delivered status semantics differ by provider, country, carrier, and route. + - Phone numbers may be reassigned, shared, forwarded, or unavailable. + - Link clicks do not prove actor identity. + - SMS content may be filtered by carriers or blocked by compliance rules. + - Alphanumeric sender IDs may not support inbound replies. +``` + +## 16. Action Request Handling + +### 16.1 `notification.send` + +`notification.send` sends a new SMS notification. + +Required fields: + +```text +request_id +action_type +coordination_case_id +participant_id +target_endpoint +content or template_ref +tracking_context +idempotency_key +requested_at +``` + +Example: + +```yaml +request_id: req_001 +action_type: notification.send +coordination_case_id: case_123 +participant_id: participant_456 +channel: sms +target_endpoint: + endpoint_type: phone_number + value: "+491701234567" +template_ref: secure-document-sms-reminder +variables: + portal_link: https://portal.example.com/access/abc +tracking_context: + correlation_id: corr_789 + coordination_case_id: case_123 + participant_id: participant_456 + notification_id: notif_001 + payload_id: payload_777 +idempotency_key: case_123:participant_456:sms:notif_001 +requested_at: 2026-01-01T12:00:00Z +``` + +### 16.2 Action Result + +The adapter returns: + +```yaml +request_id: req_001 +adapter_id: sms-connect.default +accepted: true +action_state: accepted +adapter_operation_id: smsop_001 +provider_operation_id: providerop_001 +initial_events: + - event_type: notification.attempt.accepted_by_adapter +received_at: 2026-01-01T12:00:01Z +``` + +The result does not prove the SMS was delivered or read. + +## 17. Provider Abstraction + +`sms-connect` SHOULD support a provider abstraction layer. + +Provider integration responsibilities: + +* send SMS messages +* store provider message IDs +* verify webhooks +* ingest delivery receipts +* normalize provider statuses +* parse error codes +* handle opt-out events +* handle inbound replies +* expose provider health +* expose route/country/sender limitations + +Provider model: + +```yaml +SmsProvider: + provider_name: string + provider_account_ref: string + supported_features: + - sending + - delivery_receipts + - inbound_sms + - opt_out_handling + - sender_pools + - short_codes + - toll_free + - alphanumeric_sender_id + - scheduled_send + - status_callbacks + event_mapping_ref: string + configuration_ref: string +``` + +The first implementation MAY use a simulated provider. Real providers SHOULD be added behind this abstraction. + +## 18. Native Provider Event Mapping + +The adapter MUST support provider-specific mapping files or code modules. + +Common provider-native status groups: + +```text +accepted +queued +scheduled +sending +sent +dispatched +carrier_accepted +buffered +delivered +undelivered +failed +rejected +expired +unknown +deleted +opted_out +inbound +``` + +Important mapping rule: + +Provider `delivered` events MUST be interpreted carefully. + +For SMS, `delivered` usually means a provider or downstream network reports successful delivery according to its route-specific semantics. It SHOULD map to: + +```text +notification.endpoint.accepted +``` + +with delivery metadata and strong endpoint evidence, but not to coordination result success unless the scenario policy explicitly accepts SMS DLR as sufficient. + +## 19. Delivery Receipt Classification + +`sms-connect` SHOULD classify delivery receipts into structured outcomes. + +```yaml +SmsDeliveryReceipt: + provider_name: string + provider_message_id: string + provider_status: string + provider_error_code: string? + provider_error_text: string? + carrier_status: string? + carrier_error_code: string? + occurred_at: timestamp + final: boolean? + retryable: boolean? + classification: + - delivered + - accepted + - buffered + - deferred + - expired + - failed + - rejected + - unknown + - unknown_subscriber + - unavailable_subscriber + - unreachable_subscriber + - blocked + - filtered + - invalid_number + - route_unavailable + - sender_not_allowed + - content_not_allowed + - regulatory_rejected + - temporary_network_error + - provider_error +``` + +Suggested mappings: + +| DLR class | SMS assessment | Normalized event | +| ----------------------- | ------------------------------------------------- | ----------------------------------------------------------------- | +| delivered | `success.delivered` | `notification.endpoint.accepted` | +| accepted | `undef.carrier_accepted_only` | `notification.endpoint.accepted` | +| buffered | `undef.buffered` | `notification.endpoint.deferred` | +| deferred | `undef.deferred` | `notification.endpoint.deferred` | +| expired | `fail.expired` or `undef.expired_unknown` | `notification.endpoint.unknown` or rejected | +| failed | `fail.delivery_failed` | `notification.endpoint.rejected_permanent` | +| rejected | `fail.carrier_rejected` | `notification.endpoint.rejected_permanent` | +| unknown | `undef.dlr_unknown` | `notification.endpoint.unknown` | +| unknown_subscriber | `fail.unknown_subscriber` | `notification.endpoint.rejected_permanent` | +| unavailable_subscriber | `undef.deferred` or `fail.unavailable_subscriber` | temporary or permanent by provider code | +| filtered | `fail.filtered_by_carrier` | `notification.endpoint.rejected_permanent` | +| sender_not_allowed | `fail.sender_not_allowed` | `notification.attempt.rejected_by_provider` or endpoint rejection | +| regulatory_rejected | `fail.policy_rejected` | `notification.endpoint.rejected_permanent` | +| temporary_network_error | `undef.deferred` | `notification.endpoint.rejected_temporary` | + +## 20. Consent and Suppression Model + +`sms-connect` SHOULD maintain or integrate with a consent and suppression model. + +```yaml +SmsConsent: + endpoint_ref: EndpointRef + consent_state: opted_in | opted_out | unknown | not_required | disputed + consent_scope: global | tenant | sender_identity | campaign | coordination_case | purpose + consent_source: form | sms_keyword | contract | import | operator | external_system | unknown + consent_acquired_at: timestamp? + consent_evidence_ref: string? + expires_at: timestamp? +``` + +```yaml +SmsSuppression: + endpoint_ref: EndpointRef + suppression_type: opt_out | hard_failure | complaint | manual | provider_policy | consent_missing | unknown + scope: global | tenant | sender_identity | campaign | coordination_case | purpose + reason: string? + source: provider | adapter | operator | participant | policy + created_at: timestamp + expires_at: timestamp? +``` + +Suppression should produce evidence: + +```text +notification.channel.suppression_added +notification.channel.suppression_removed +notification.channel.unsubscribe_received +``` + +coordination-engine decides whether suppression means participant failure, channel failure, alternate channel selection, or manual review. + +## 21. Sender Identity and Campaign Model + +SMS deliverability and compliance often depend on sender identity and campaign registration. + +`sms-connect` SHOULD support sender identity metadata. + +```yaml +SmsSenderIdentity: + sender_identity_id: string + sender_type: long_code | short_code | toll_free | alphanumeric_sender_id | sender_pool | unknown + sender_value: string? + country_scope: + - string + registration_state: unknown | not_required | pending | registered | rejected | expired + use_case: transactional | informational | marketing | verification | alert | mixed | unknown + brand_ref: string? + campaign_ref: string? + provider_account_ref: string? + throughput_profile_ref: string? + limitations: + - string +``` + +Examples of limitations: + +```text +inbound_replies_not_supported +country_not_supported +marketing_not_allowed +registration_required +low_throughput +content_filtering_risk +``` + +## 22. Message Content, Encoding, and Segmentation + +`sms-connect` SHOULD analyze message content before sending. + +```yaml +SmsContentAnalysis: + body_length: integer + encoding: gsm7 | ucs2 | binary | unknown + segment_count: integer + per_segment_limit: integer + contains_url: boolean + contains_tracking_link: boolean + contains_opt_out_text: boolean + contains_non_gsm_characters: boolean + estimated_cost_units: number? + warnings: + - string +``` + +Potential warnings: + +```text +message_requires_multiple_segments +unicode_reduces_segment_capacity +tracking_link_may_increase_length +message_missing_required_opt_out_text +content_filtering_risk +sender_identity_not_allowed_for_replies +``` + +Content analysis should not by itself determine coordination success, but it is important for cost, reliability, compliance, and diagnostics. + +## 23. Link Tracking Model + +SMS has no native open tracking. Link tracking may provide interaction evidence if a tracked URL is included. + +```yaml +SmsLinkClickEvent: + sms_message_id: string + link_id: string + occurred_at: timestamp + url: string + ip_address: string? + user_agent: string? + scanner_classification: none | suspected | likely | confirmed | unknown + confidence: low | medium | high +``` + +Possible classifications: + +```text +human_like_click +scanner_click +link_preview_fetch +unverified_click +unknown_click +``` + +The adapter SHOULD classify link clicks conservatively. + +A click is not identity-bound unless paired with trusted identity evidence from the action surface. + +## 24. Inbound Reply Model + +`sms-connect` SHOULD support inbound SMS where the sender type and route support replies. + +```yaml +SmsInboundMessage: + inbound_message_id: string + provider_name: string + provider_message_id: string? + from_endpoint: SmsEndpoint + to_endpoint: SmsEndpoint + received_at: timestamp + body_text: string + keyword_classification: none | opt_out | opt_in | help | acknowledgement | free_text | unknown + correlation: CorrelationContext? + raw_event_ref: string? +``` + +Keyword classifications: + +```text +STOP / UNSUBSCRIBE / CANCEL / END / QUIT +START / UNSTOP +HELP / INFO +YES / OK / ACK / CONFIRM +NO / DECLINE +``` + +Actual keyword handling should be configurable by country, provider, language, sender identity, and scenario. + +## 25. Phone Endpoint Quality + +`sms-connect` SHOULD maintain phone endpoint quality signals. + +```yaml +SmsEndpointQuality: + endpoint_ref: EndpointRef + e164_valid: boolean? + country_code: string? + number_type: mobile | landline | voip | toll_free | unknown + reachable: boolean? + carrier_name: string? + ported: boolean? + roaming: boolean? + consent_state: string? + suppression_state: string? + last_provider_acceptance_at: timestamp? + last_carrier_acceptance_at: timestamp? + last_delivered_at: timestamp? + last_failed_at: timestamp? + last_opt_out_at: timestamp? + last_reply_at: timestamp? +``` + +Endpoint quality may be emitted as diagnostics but should not by itself create coordination success. + +## 26. Channel Health + +`sms-connect` SHOULD expose channel health. + +```yaml +SmsChannelHealth: + sender_identity_ref: string + provider_account_ref: string? + country_scope: string? + status: healthy | degraded | failing | unknown + registration_status: registered | pending | rejected | expired | not_required | unknown + route_status: healthy | degraded | unavailable | unknown + deliverability_status: good | warning | poor | unknown + opt_out_rate: number? + failure_rate: number? + unknown_rate: number? + filtering_rate: number? + provider_degradation: + - string +``` + +Health-related normalized events: + +```text +notification.channel.reputation_warning +system.provider.degraded +system.provider.unavailable +system.adapter.health_changed +``` + +## 27. Security Requirements + +`sms-connect` MUST: + +* protect provider credentials +* verify provider webhooks where possible +* validate inbound provider event authenticity where possible +* preserve idempotency +* prevent duplicate sends for repeated idempotency keys +* protect opt-out and consent records +* avoid leaking phone numbers in logs where possible +* avoid exposing tracking tokens unnecessarily +* sanitize inbound SMS bodies before processing +* support tenant or sender separation where applicable + +## 28. Privacy Requirements + +`sms-connect` SHOULD: + +* store message content only when necessary +* support metadata-only mode +* support raw event redaction +* support configurable retention of raw provider events +* store endpoint references instead of full phone numbers where possible +* support masking of phone numbers +* support deletion or anonymization workflows +* separate operational diagnostics from coordination evidence +* avoid unnecessary retention of link-click metadata +* allow link tracking to be disabled + +## 29. Reliability Requirements + +`sms-connect` MUST support: + +* idempotent send requests +* duplicate webhook event detection +* out-of-order event handling +* late DLR handling +* retryable provider failures +* non-retryable provider failures +* provider timeout handling +* correlation preservation +* dead-letter handling for unprocessable events +* explicit timeout/expiry handling for missing final DLRs + +Late events MUST be preserved. + +Example: + +```text +Provider accepted at 10:00 +No DLR by 12:00 +Participant unresolved at 14:00 +Delivery receipt arrives at 18:00 +``` + +The late DLR must still be recorded and emitted as evidence. + +## 30. Raw Event Preservation + +`sms-connect` SHOULD preserve raw provider events or references to them. + +```yaml +RawSmsEventRef: + raw_event_id: string + provider_name: string + storage_ref: string? + received_at: timestamp + redacted: boolean +``` + +Normalized events should reference raw event data where available: + +```yaml +raw_event_ref: raw_sms_event_123 +``` + +## 31. Minimal API Surface + +`sms-connect` SHOULD expose a headless API. + +### 31.1 Adapter Contract API + +Required conceptual operations: + +```text +GET /adapter/descriptor +GET /adapter/health +POST /adapter/actions +POST /adapter/events/provider +GET /adapter/events +GET /adapter/messages/{id}/timeline +GET /adapter/messages/{id}/assessment +``` + +The actual transport may differ, but these conceptual operations should exist. + +### 31.2 Standalone API + +Useful standalone operations: + +```text +POST /sms/send +GET /sms/messages/{id} +GET /sms/messages/{id}/timeline +GET /sms/messages/{id}/assessment +GET /sms/endpoints/{id}/quality +POST /sms/suppressions +DELETE /sms/suppressions/{id} +POST /sms/consents +GET /sms/channel-health +GET /sms/content/analyze +``` + +## 32. Example End-to-End Flows + +### 32.1 Secure Document Reminder + +1. `coordination-engine` creates a coordination case. +2. Email notification remains unresolved after threshold. +3. Policy engine selects SMS fallback. +4. `coordination-engine` sends `notification.send` to `sms-connect`. +5. `sms-connect` validates consent, suppression, sender identity, content, and segmentation. +6. Provider accepts the SMS. +7. `sms-connect` emits `notification.attempt.accepted_by_provider`. +8. Carrier reports delivery. +9. `sms-connect` emits `notification.endpoint.accepted` with delivered metadata. +10. Recipient clicks link. +11. `sms-connect` emits `interaction.unverified_actor_interaction`. +12. Recipient authenticates in portal. +13. `portal-connect` emits `identity.actor_authenticated`. +14. Recipient downloads document. +15. `portal-connect` emits `delivery.payload.downloaded`. +16. `coordination-engine` marks participant complete. + +SMS contributed stronger notification evidence than email, but the portal event closes the result. + +### 32.2 Undelivered SMS and Alternate Channel + +1. `sms-connect` sends SMS. +2. Provider accepts the message. +3. Carrier returns unknown subscriber. +4. `sms-connect` emits `notification.endpoint.rejected_permanent`. +5. `coordination-engine` marks SMS channel failed for that participant. +6. Policy engine selects alternate channel or manual review. + +### 32.3 SMS Acknowledgement + +1. `coordination-engine` sends alert via SMS. +2. Carrier reports delivery. +3. Participant replies `ACK`. +4. `sms-connect` emits `interaction.acknowledgement_recorded`. +5. If scenario policy accepts SMS acknowledgement, `coordination-engine` marks the participant as acknowledged. +6. If stronger identity is required, the participant remains incomplete until identity-bound evidence is provided. + +### 32.4 Opt-Out Handling + +1. Participant replies `STOP`. +2. `sms-connect` records opt-out and adds suppression. +3. `sms-connect` emits `notification.channel.unsubscribe_received` and `notification.channel.suppression_added`. +4. `coordination-engine` updates channel viability. +5. Policy engine selects fallback channel or manual review depending on scenario. + +## 33. Provider Implementation Guidance + +The first real provider integration SHOULD be selected based on: + +* delivery receipt quality +* clear status callbacks +* error-code detail +* inbound SMS support +* opt-out handling support +* sender identity support +* country coverage +* webhook security +* correlation metadata support +* cost and local availability +* compatibility with transactional and high-assurance notifications + +The implementation SHOULD avoid hardcoding provider semantics into the core SMS model. + +Provider-specific modules should map to the SMS-native model first, then to normalized coordination events. + +```text +Provider event +→ sms-native event +→ SmsEvidenceAssessment +→ EvidenceEvent for coordination-engine +``` + +## 34. Message Stream Separation + +`sms-connect` SHOULD support sender identities, campaign scopes, and message streams. + +Recommended streams: + +```text +transactional +notification +legal_or_high_assurance_notice +marketing +verification +system_alert +test +``` + +High-assurance or legally relevant notifications SHOULD NOT share th sender configuration with marketing traffic unless explicitly accepted by policy. + +Stream separation may affect: + +* sender number +* sender pool +* short code +* toll-free number +* alphanumeric sender ID +* campaign registration +* consent scope +* opt-out scope +* throughput +* carrier filtering risk +* delivery diagnostics + +## 35. Legal and Compliance Disclaimer + +`sms-connect` does not by itself provide legal proof of delivery, legal notice, acceptance, signature, payment, or contract closure. + +It provides evidence from the SMS channel. + +Scenario-specific applications and `coordination-engine` policies may combine SMS evidence with stronger evidence from portal, identity, signature, payment, archive, or manual processes. + +The adapter MUST avoid naming technical SMS events in ways that imply legal success. + +Use: + +```text +notification.endpoint.accepted +``` + +Avoid: + +```text +recipient_legally_notified +delivery_legally_completed +human_read_confirmed +``` + +## 36. MVP Scope + +The first useful version of `sms-connect` should implement: + +1. Adapter descriptor. +2. Adapter health endpoint. +3. `notification.send`. +4. Idempotent send request handling. +5. Simulated provider or one real provider. +6. SMS message and attempt records. +7. Provider event ingestion. +8. Basic delivery receipt classification. +9. Basic phone endpoint validation. +10. Basic segmentation and encoding analysis. +11. Basic opt-out/suppression support. +12. Basic inbound reply support if provider allows. +13. Evidence event generation. +14. Message timeline. +15. Message assessment. +16. Mapping to AdapterInterfaceSpecification.md v1.0. + +### MVP Required SMS Events + +```text +notification.attempt.accepted_by_adapter +notification.attempt.rejected_by_adapter +notification.attempt.accepted_by_provider +notification.attempt.rejected_by_provider +notification.attempt.queued +notification.endpoint.accepted +notification.endpoint.deferred +notification.endpoint.rejected_temporary +notification.endpoint.rejected_permanent +notification.endpoint.unknown +notification.channel.unsubscribe_received +notification.channel.suppression_added +interaction.reply_received +interaction.acknowledgement_recorded +interaction.unverified_actor_interaction +``` + +### MVP Acceptance Criteria + +The MVP is acceptable when it can: + +1. Accept a coordination-compatible SMS send request. +2. Dispatch or simulate an SMS. +3. Preserve correlation and idempotency. +4. Ingest or simulate provider delivery receipts. +5. Produce normalized evidence events. +6. Classify provider acceptance as weak evidence. +7. Classify delivered DLR as strong endpoint evidence but not human-awareness proof. +8. Classify final undelivered/failed/rejected states as negative channel evidence. +9. Classify unknown/no-DLR cases as unresolved. +10. Record STOP/opt-out as suppression evidence. +11. Record inbound replies as interaction evidence. +12. Provide a message timeline. +13. Provide an SMS evidence assessment. +14. Integrate with coordination-engine without overclaiming success. + +## 37. Future Extensions + +Potential future capabilities: + +* multi-provider routing +* provider failover +* country-specific routing rules +* sender pool optimization +* throughput management +* advanced number lookup +* number reassignment risk detection +* HLR or carrier lookup integration where lawful and available +* advanced opt-in/opt-out workflow +* advanced keyword routing +* two-way conversational SMS support +* natural-language reply classification +* URL shortening and tracking integration +* adaptive fallback between email, SMS, push, and voice +* RCS adapter extension or separate `rcs-connect` +* MMS support +* WhatsApp or messaging-app adapters +* regulatory diagnostics by country +* A2P registration diagnostics +* carrier filtering risk scoring +* cost forecasting by segment count and country +* consent evidence archive integration + +## 38. Non-Goals + +`sms-connect` is not: + +* a marketing automation platform +* a full conversational messaging platform +* a CRM +* a full workflow engine +* a legal notice system by itself +* a payment system +* a signature system +* an identity verification system by itself +* the owner of coordination case success + +It may integrate with such systems or be used by them. + +## 39. Summary + +`sms-connect` models SMS as a useful but still uncertain notification, reminder, and lightweight interaction channel. + +Its job is to: + +* send SMS messages +* ingest provider and carrier delivery receipts +* classify SMS outcomes +* normalize SMS evidence +* manage opt-out and suppression state +* record inbound replies +* analyze content, encoding, and segmentation +* expose endpoint and channel diagnostics +* integrate cleanly with `coordination-engine` + +The key rule is: + +> SMS events are evidence, not result satisfaction. sms-connect reports SMS-channel facts and uncertainty. coordination-engine evaluates intended results. + +[1]: https://help.twilio.com/articles/223134347-What-are-the-Possible-SMS-and-MMS-Message-Statuses-and-What-do-They-Mean-?utm_source=chatgpt.com "What are the possible SMS and MMS message statuses ..." +[2]: https://developer.vonage.com/en/messaging/sms/guides/delivery-receipts?utm_source=chatgpt.com "SMS Delivery Receipts API Guide" +[3]: https://developers.sinch.com/docs/sms/api-reference/sms/delivery-reports/deliveryreport?utm_source=chatgpt.com "Delivery Report" +[4]: https://www.twilio.com/docs/glossary/what-sms-character-limit?utm_source=chatgpt.com "How long can a message be? | Twilio" +[5]: https://www.twilio.com/docs/messaging/tutorials/advanced-opt-out?utm_source=chatgpt.com "Customize users' opt-in and opt-out experience with ..." + diff --git a/spec/XmppAdapterSpecification.md b/spec/XmppAdapterSpecification.md new file mode 100644 index 0000000..4b329b6 --- /dev/null +++ b/spec/XmppAdapterSpecification.md @@ -0,0 +1,1621 @@ +# XmppAdapterSpecification.md + +## 1. Document Status + +**Document:** XmppAdapterSpecification.md +**Project:** xmpp-connect +**Target Integration:** coordination-engine +**Adapter Contract:** AdapterInterfaceSpecification.md v1.0 +**Status:** Draft v1.0 +**Primary Scope:** XMPP messaging, presence, delivery receipts, displayed markers, multi-user chat, PubSub, and XMPP event normalization as a coordination-engine adapter + +## 2. Purpose + +This document specifies how `xmpp-connect` models XMPP as a communication protocol and how it integrates with `coordination-engine`. + +`xmpp-connect` is an adapter for near-real-time messaging, presence-aware communication, system-to-system signaling, lightweight interaction, group communication, and publish-subscribe communication through XMPP. + +XMPP can provide richer interaction evidence than email or RSS because it has native concepts for authenticated sessions, JIDs, resources, presence, message stanzas, delivery receipts, displayed markers, chat state, group rooms, and PubSub nodes. However, XMPP still does not automatically prove human awareness, comprehension, legal receipt, payload retrieval, signature, payment, or coordination result satisfaction. + +The key design objective is to make XMPP useful as a notification, communication, presence, and lightweight interaction adapter while preserving uncertainty and avoiding overclaiming result success. + +## 3. Core Principle + +XMPP is a presence-aware, near-real-time messaging and eventing protocol. It can provide stronger endpoint, session, and client-level evidence than email or RSS, but adapter events remain evidence, not coordination result satisfaction. + +The adapter MUST distinguish: + +```text +server acceptance +routeability to bare JID or full JID +resource/session availability +presence evidence +client delivery receipt +displayed marker +acknowledgement marker +chat state +MUC room delivery +PubSub publication +inbound reply or command +identity-bound evidence from authenticated XMPP sessions +result evidence from other action surfaces +``` + +Delivery to an XMPP client controlled by a recipient can be strong notification evidence. A displayed marker can be stronger awareness evidence. But coordination-engine remains responsible for deciding whether this satisfies the intended result. + +## 4. Architectural Role + +### 4.1 Standalone Role + +As a standalone component, `xmpp-connect` provides: + +* provider-neutral XMPP client or component integration +* XMPP message sending +* XMPP stanza tracking +* delivery receipt request and processing +* displayed marker request and processing where supported +* presence observation +* inbound message and command handling +* MUC room message support +* optional PubSub publication +* message timeline and evidence assessment +* JID endpoint diagnostics +* XMPP server and stream health diagnostics +* normalized XMPP evidence events + +### 4.2 coordination-engine Adapter Role + +As a `coordination-engine` adapter, `xmpp-connect` provides: + +#### Actions + +* `notification.send` +* `notification.send_reminder` +* `notification.schedule` where supported externally +* `notification.cancel` where queued locally and not yet sent +* `interaction.request_acknowledgement` +* `interaction.send_command_prompt` +* `feed.publish_item` for XMPP PubSub where used +* `notification.register_tracking_context` +* `recipient.suppress` +* `recipient.unsuppress` + +#### Signals + +* `notification.attempt.accepted_by_adapter` +* `notification.attempt.rejected_by_adapter` +* `notification.attempt.accepted_by_provider` +* `notification.attempt.rejected_by_provider` +* `notification.endpoint.accepted` +* `notification.endpoint.deferred` +* `notification.endpoint.rejected_temporary` +* `notification.endpoint.rejected_permanent` +* `notification.endpoint.unreachable` +* `notification.endpoint.unknown` +* `interaction.reply_received` +* `interaction.acknowledgement_recorded` +* `interaction.notification.opened` +* `interaction.authenticated_actor_interaction` +* `interaction.unverified_actor_interaction` +* `identity.actor_authenticated` +* `feed.item.published` +* `feed.item.fetched` where PubSub subscriber evidence is available +* `system.provider.degraded` +* `system.provider.unavailable` + +## 5. Relationship to coordination-engine + +`xmpp-connect` does not own: + +* `CoordinationCase` +* intended result evaluation +* participant-level success +* case-level success +* legal receipt interpretation +* portal payload retrieval +* payment settlement +* contract signature +* final acknowledgement policy +* multi-channel escalation logic + +`xmpp-connect` owns: + +* XMPP connection and stream management +* XMPP server/provider abstraction +* JID and resource modeling +* stanza sending +* stanza IDs and correlation +* delivery receipt handling +* displayed marker handling +* presence tracking +* inbound message parsing +* MUC and PubSub event mapping +* XMPP-native message timeline +* XMPP evidence mapping +* XMPP-specific uncertainty classification + +The boundary rule is: + +> xmpp-connect reports what happened in the XMPP channel and what that may indicate. coordination-engine decides what that means for the coordination case. + +## 6. XMPP as Notification, Communication, Presence, and Event Channel + +Within `coordination-engine`, XMPP can serve several roles: + +```text +notification channel +interactive message channel +presence-aware channel +system-to-system event channel +group/room channel +PubSub publication channel +lightweight acknowledgement channel +``` + +XMPP may carry a small payload inline, but for high-assurance or sensitive scenarios it should usually point to an authenticated action surface. + +Examples: + +```text +XMPP message: "A document is available." +XMPP message: "Please acknowledge this incident." +XMPP command prompt: "Reply ACK to confirm." +MUC room message: "New incident case opened." +PubSub item: "New coordination event available." +``` + +XMPP may provide stronger evidence than email if the recipient’s authenticated client sends a delivery receipt or displayed marker. Still, payload access and business result evidence should normally come from stronger domain adapters such as `portal-connect`, `signature-connect`, or `payment-connect`. + +## 7. XMPP Technology Scope + +### 7.1 Core XMPP + +The adapter should model XMPP’s core concepts: + +```text +XML stream +message stanza +presence stanza +IQ stanza +JID +bare JID +full JID +resource +server-to-server routing +client-to-server routing +authentication +stream errors +stanza errors +``` + +### 7.2 Instant Messaging and Presence + +The adapter should support the basic instant messaging and presence semantics defined by XMPP IM: + +```text +one-to-one chat +presence availability +subscription state +roster/contact availability where applicable +offline message behavior where supported +``` + +### 7.3 Delivery Receipts + +The adapter SHOULD support XEP-0184 Message Delivery Receipts where the remote client supports it. + +A delivery receipt indicates that a content message was delivered to a client controlled by the intended recipient. It does not prove human reading or result satisfaction. + +### 7.4 Displayed Markers + +The adapter MAY support XEP-0333 Displayed Markers where the remote client supports it. + +A displayed marker can provide stronger evidence that the message was displayed to the user by a client, but it still does not prove comprehension, acceptance, or payload access. + +### 7.5 Stream Management + +The adapter SHOULD support XEP-0198 Stream Management where available. + +Stream management can provide transport-level acknowledgement between client and server and improve reliability after connection interruptions. It should not be confused with recipient delivery or human awareness. + +### 7.6 Chat State Notifications + +The adapter MAY support XEP-0085 Chat State Notifications. + +Chat states such as active, composing, paused, inactive, or gone can provide interaction context, but they are generally weak evidence and should not be used as result completion. + +### 7.7 Message Archive Management + +The adapter MAY support XEP-0313 Message Archive Management where available. + +Message archive evidence can support reconstructability, but archived presence of a message does not necessarily prove recipient awareness. + +### 7.8 Multi-User Chat + +The adapter MAY support XEP-0045 Multi-User Chat. + +MUC introduces room-level delivery and occupant-level uncertainty. A message sent to a room is not automatically delivered to every intended participant. + +### 7.9 Publish-Subscribe + +The adapter MAY support XEP-0060 Publish-Subscribe. + +XMPP PubSub resembles RSS/WebSub in that publication and subscription evidence must be distinguished from participant-specific awareness. + +## 8. XMPP Message Lifecycle Model + +`xmpp-connect` models an XMPP notification or message as a lifecycle with observable phases. + +```text +message.created +message.rendered +message.render_failed +message.send_requested +message.accepted_by_adapter +message.rejected_by_adapter +message.stream_available +message.stream_unavailable +message.sent_to_server +message.server_acknowledged +message.route_attempted +message.endpoint_available +message.endpoint_unavailable +message.delivered_to_client +message.displayed_by_client +message.acknowledged_by_client +message.reply_received +message.command_received +message.presence_observed +message.deferred_offline +message.offline_stored +message.delivery_failed +message.stanza_error_received +message.expired +message.unknown +``` + +The lifecycle is not strictly linear. XMPP messages may be routed to multiple resources, stored offline, archived, delivered later, or acknowledged by one resource but not another. + +## 9. XMPP Message vs Attempt vs Stanza vs Resource + +The adapter MUST distinguish at least four layers. + +### 9.1 XmppMessage + +The logical message created by a client or coordination case. + +```yaml +XmppMessage: + xmpp_message_id: string + coordination_case_id: string? + participant_id: string? + purpose: string? + message_body_ref: string? + template_ref: string? + tracking_context: TrackingContext + created_at: timestamp +``` + +### 9.2 XmppAttempt + +One send attempt through one XMPP connection, account, component, or provider. + +```yaml +XmppAttempt: + xmpp_attempt_id: string + xmpp_message_id: string + xmpp_account_ref: string + xmpp_server_ref: string? + from_jid: XmppEndpoint + to_jid: XmppEndpoint + message_type: chat | normal | headline | groupchat | error + stanza_id: string? + origin_id: string? + server_id: string? + state: XmppAttemptState + created_at: timestamp + updated_at: timestamp +``` + +### 9.3 XmppStanza + +One protocol-level stanza associated with the attempt. + +```yaml +XmppStanza: + stanza_ref: string + stanza_type: message | presence | iq + message_type: chat | normal | headline | groupchat | error | unknown + stanza_id: string? + origin_id: string? + stable_id: string? + from_jid: string + to_jid: string + payload_ref: string? + sent_at: timestamp? + received_at: timestamp? + raw_stanza_ref: string? +``` + +### 9.4 XmppEndpoint + +The XMPP target or sender endpoint. + +```yaml +XmppEndpoint: + endpoint_id: string? + endpoint_type: bare_jid | full_jid | muc_room_jid | pubsub_node | component_jid | unknown + jid: string + bare_jid: string? + resource: string? + display_name: string? + verification_state: unknown | syntax_validated | roster_known | presence_known | authenticated | invalid + suppression_state: active | suppressed | blocked | unsubscribed | unknown + metadata: object? +``` + +A bare JID identifies an account. A full JID identifies a specific resource/session. This distinction is central to evidence interpretation. + +## 10. XMPP Attempt States + +The adapter SHOULD support these attempt states: + +```text +created +rendered +render_failed +send_requested +accepted_by_adapter +rejected_by_adapter +stream_unavailable +sent_to_server +server_acknowledged +route_attempted +endpoint_available +endpoint_unavailable +offline_stored +deferred +delivered_to_client +displayed_by_client +acknowledged_by_client +reply_received +stanza_error_received +failed +expired +unknown +``` + +These states are XMPP-native. They are not coordination result states. + +## 11. XMPP Evidence Assessment + +`xmpp-connect` should provide an XMPP-native assessment separate from coordination-engine state. + +```yaml +XmppEvidenceAssessment: + xmpp_message_id: string + participant_id: string? + category: success | fail | undef + subclass: string + confidence: low | medium | high + strongest_signal: string? + evidence_summary: + - string + recommended_coordination_interpretation: string? +``` + +The assessment categories are adapter-level hints. + +### 11.1 XMPP Adapter Success + +XMPP-level `success` means the XMPP channel produced meaningful XMPP-channel evidence. + +Possible subclasses: + +```text +success.sent_to_server +success.server_acknowledged +success.delivered_to_client +success.displayed_by_client +success.acknowledged_by_client +success.reply_received +success.presence_observed +success.pubsub_item_published +``` + +Important: These are not automatically coordination success. + +`success.delivered_to_client` is strong notification evidence but not human-awareness proof unless scenario policy accepts client delivery as sufficient. + +`success.displayed_by_client` can be stronger awareness evidence, but it still does not prove comprehension or business result satisfaction. + +### 11.2 XMPP Adapter Fail + +XMPP-level `fail` indicates strong evidence that the XMPP channel failed or should not be used. + +Subclasses: + +```text +fail.invalid_jid +fail.missing_jid +fail.stream_unavailable +fail.authentication_failed +fail.authorization_failed +fail.remote_server_unreachable +fail.recipient_not_found +fail.recipient_blocked +fail.not_in_roster +fail.policy_rejected +fail.stanza_error +fail.delivery_failed +fail.muc_room_unavailable +fail.muc_forbidden +fail.pubsub_node_unavailable +fail.expired +fail.suppressed +``` + +### 11.3 XMPP Adapter Undef + +XMPP-level `undef` is used when the adapter cannot determine final delivery, display, human awareness, or intended-recipient action. + +Subclasses: + +```text +undef.pending +undef.sent_to_server_only +undef.server_acknowledged_only +undef.no_receipt_requested +undef.receipt_requested_no_response +undef.resource_unknown +undef.bare_jid_routing_unknown +undef.offline_stored +undef.offline_delivery_unknown +undef.presence_unavailable +undef.presence_available_but_no_receipt +undef.delivered_but_awareness_unproven +undef.displayed_but_comprehension_unproven +undef.reply_identity_uncertain +undef.muc_room_message_participant_uncertain +undef.pubsub_publication_only +undef.conflicting_evidence +undef.channel_degraded +``` + +The `undef` category must not be empty because XMPP delivery semantics depend on client support, resource routing, server behavior, offline storage, MUC behavior, and extension support. + +## 12. Detailed XMPP Scenario Classification + +### 12.1 Pre-Send Scenarios + +| Scenario | XMPP assessment | Normalized event | Notes | +| -------------------------------------- | -------------------------------------------- | ------------------------------------------ | ----------------------------------------------------- | +| Missing JID | `fail.missing_jid` | `notification.attempt.rejected_by_adapter` | No send possible | +| Invalid JID syntax | `fail.invalid_jid` | `notification.attempt.rejected_by_adapter` | Strong local failure | +| Template/rendering failure | `fail.stanza_error` or render fail | `notification.attempt.rejected_by_adapter` | Message unusable | +| Missing XMPP account config | `fail.authentication_failed` | action error | Operational failure | +| Adapter stream unavailable | `fail.stream_unavailable` or `undef.pending` | `system.provider.unavailable` | Depends on retry policy | +| Recipient suppressed/blocked locally | `fail.suppressed` | `notification.channel.suppression_added` | Channel blocked | +| Message requires unsupported extension | `fail.policy_rejected` or warning | action rejection or metadata | Example: mandatory receipt/display marker unsupported | + +### 12.2 Stream and Server Scenarios + +| Scenario | XMPP assessment | Normalized event | Notes | +| -------------------------- | ------------------------------------ | ------------------------------------------------------- | ----------------------------------------------- | +| Adapter accepted request | `undef.pending` | `notification.attempt.accepted_by_adapter` | Adapter accepted work | +| XMPP stream connected | neutral/positive health | `system.provider.degraded` cleared or metadata | Connection usable | +| XMPP stream unavailable | `fail.stream_unavailable` or pending | `system.provider.unavailable` | Operational failure or retry | +| SASL authentication failed | `fail.authentication_failed` | `notification.attempt.rejected_by_adapter` | Strong operational failure | +| TLS/security failure | `fail.stream_unavailable` | `system.provider.unavailable` | Strong operational/security failure | +| Message sent to server | `undef.sent_to_server_only` | `notification.attempt.accepted_by_provider` | Server accepted outbound stanza | +| Stream management ack | `undef.server_acknowledged_only` | `notification.attempt.accepted_by_provider` | Server/stream-level ack, not recipient delivery | +| Remote server unreachable | `fail.remote_server_unreachable` | `notification.endpoint.unreachable` | Strong or retryable depending error | +| Stanza error received | `fail.stanza_error` | `notification.endpoint.rejected_permanent` or temporary | Depends on stanza error type | + +### 12.3 Recipient and Resource Scenarios + +| Scenario | XMPP assessment | Normalized event | Notes | +| ---------------------------------------------- | ----------------------------------- | ------------------------------------------------------- | --------------------------------------------- | +| Recipient bare JID exists but resource unknown | `undef.bare_jid_routing_unknown` | `notification.endpoint.unknown` | Server may route to resource or store offline | +| Full JID resource available | stronger endpoint evidence | `notification.endpoint.accepted` | Specific client/session target known | +| Recipient presence available | `success.presence_observed` or weak | `notification.endpoint.accepted` with metadata | Presence does not prove message delivery | +| Recipient presence unavailable | `undef.presence_unavailable` | `notification.endpoint.deferred` or unknown | Offline storage may apply | +| Message stored offline | `undef.offline_stored` | `notification.endpoint.deferred` | Delivery pending/unknown | +| Offline storage unsupported | `fail.delivery_failed` | `notification.endpoint.rejected_temporary` or permanent | Depends on server | +| Recipient blocked sender | `fail.recipient_blocked` | `notification.endpoint.rejected_permanent` | Strong channel failure | +| Recipient not in roster when required | `fail.not_in_roster` | `notification.endpoint.rejected_permanent` | Policy or server behavior | +| Recipient account not found | `fail.recipient_not_found` | `notification.endpoint.rejected_permanent` | Strong endpoint failure | + +### 12.4 Delivery Receipt Scenarios + +| Scenario | XMPP assessment | Normalized event | Notes | +| --------------------------------- | ------------------------------------------------- | -------------------------------- | ---------------------------------- | +| Receipt requested and received | `success.delivered_to_client` | `notification.endpoint.accepted` | Strong client-delivery evidence | +| Receipt requested but no response | `undef.receipt_requested_no_response` | none or timeout event | Absence of receipt proves little | +| Client does not support receipts | `undef.no_receipt_requested` or unsupported | metadata | Capability limitation | +| Receipt from one resource only | `success.delivered_to_client` but resource-scoped | `notification.endpoint.accepted` | Bare-JID awareness still uncertain | +| Receipt delayed | update evidence late | `notification.endpoint.accepted` | Must be accepted | +| Receipt cannot be correlated | `undef.conflicting_evidence` | metadata or ignored/pending | Preserve raw event | +| Receipt spoofing risk | confidence reduced | evidence grade notes | Depends on trust boundary | + +Delivery receipts should be strong endpoint/client evidence but not proof of reading. + +### 12.5 Displayed Marker and Acknowledgement Scenarios + +| Scenario | XMPP assessment | Normalized event | Notes | +| --------------------------------------------- | ------------------------------------------------------ | -------------------------------------------------------------- | ------------------------------------- | +| Displayed marker received | `success.displayed_by_client` | `interaction.notification.opened` or authenticated interaction | Stronger awareness evidence | +| Acknowledged marker received | `success.acknowledged_by_client` | `interaction.acknowledgement_recorded` | Scenario policy decides if sufficient | +| Marker unsupported | `undef.display_marker_unavailable` | metadata | Capability limitation | +| Marker from one resource only | `success.displayed_by_client` but scoped | event with resource metadata | Multi-device ambiguity | +| Marker delayed | late evidence | event when received | Preserve timing | +| Displayed marker in MUC | participant-specific only if occupant mapping reliable | interaction event | Room semantics matter | +| Displayed marker does not prove comprehension | `undef.displayed_but_comprehension_unproven` | evidence notes | Avoid overclaiming | + +### 12.6 Inbound Reply and Command Scenarios + +| Scenario | XMPP assessment | Normalized event | Notes | +| ------------------------------------ | ------------------------------------------- | --------------------------------------------- | --------------------------------------- | +| Free-text reply received | `success.reply_received` | `interaction.reply_received` | Strong awareness evidence | +| ACK reply received | `success.acknowledged_by_client` | `interaction.acknowledgement_recorded` | May satisfy low/medium assurance policy | +| Decline command received | result-relevant negative interaction | `interaction.decline_recorded` | coordination-engine decides | +| Help/question received | `success.reply_received` | `interaction.reply_received` | May trigger support workflow | +| Reply from unknown full JID resource | `undef.reply_identity_uncertain` | `interaction.reply_received` | Actor uncertainty | +| Reply from known authenticated JID | strong | `interaction.authenticated_actor_interaction` | Stronger identity evidence | +| Reply from delegated account | identity/authority uncertain unless modeled | `interaction.reply_received` | Requires participant model | + +### 12.7 Multi-User Chat Scenarios + +| Scenario | XMPP assessment | Normalized event | Notes | +| ---------------------------------- | ---------------------------------------------- | ----------------------------------------------------- | --------------------------------- | +| Room message sent | `undef.muc_room_message_participant_uncertain` | `notification.endpoint.accepted` with room metadata | Room accepted message | +| Room unavailable | `fail.muc_room_unavailable` | `notification.endpoint.unreachable` | Strong failure | +| Sender forbidden | `fail.muc_forbidden` | `notification.endpoint.rejected_permanent` | Permission failure | +| Participant present in room | medium evidence | `notification.endpoint.accepted` or presence metadata | Presence is not reading | +| Occupant-specific displayed marker | stronger if reliable | `interaction.notification.opened` | Occupant mapping needed | +| Anonymous room | identity uncertain | event confidence low | Cannot prove participant identity | +| History replay later | `undef.delivery_pending` | metadata | Awareness timing uncertain | + +A MUC room is an action surface or group endpoint, not a guarantee of each participant’s awareness. + +### 12.8 PubSub Scenarios + +| Scenario | XMPP assessment | Normalized event | Notes | +| -------------------------------- | ------------------------------- | ------------------------------------------------------ | -------------------------------------- | +| PubSub item published | `success.pubsub_item_published` | `feed.item.published` | Publication evidence | +| Node unavailable | `fail.pubsub_node_unavailable` | `feed.hub.notification_failed` or endpoint failure | Publication failed | +| Subscriber notification accepted | medium system evidence | `notification.endpoint.accepted` or webhook-like event | Not human awareness | +| Subscriber fetches item | medium system evidence | `feed.item.fetched` | Identity depends on subscription model | +| PubSub event to client | endpoint evidence | `notification.endpoint.accepted` | May be strong system/client evidence | +| PubSub publication only | `undef.pubsub_publication_only` | `feed.item.published` | Awareness unproven | + +PubSub is closer to RSS/WebSub than direct chat, but can provide stronger system-level subscription evidence. + +## 13. Adapter-to-Coordination Mapping + +### 13.1 Core Mapping Table + +| XMPP-native event | XMPP assessment | coordination-engine event | Coordination interpretation | +| ----------------------------- | ---------------------------------------------- | ---------------------------------------------- | --------------------------------- | +| Message created | `undef.pending` | `notification.attempt.created` | Attempt exists | +| Adapter accepted | `undef.pending` | `notification.attempt.accepted_by_adapter` | Work accepted | +| Adapter rejected | adapter-specific fail | `notification.attempt.rejected_by_adapter` | Attempt failed | +| Stream unavailable | `fail.stream_unavailable` | `system.provider.unavailable` | Channel unavailable | +| Message sent to server | `undef.sent_to_server_only` | `notification.attempt.accepted_by_provider` | Server accepted outbound stanza | +| Stream management ack | `undef.server_acknowledged_only` | `notification.attempt.accepted_by_provider` | Transport/server ack only | +| Recipient presence available | `success.presence_observed` or weak | `notification.endpoint.accepted` with metadata | Availability evidence | +| Recipient unavailable | `undef.presence_unavailable` | `notification.endpoint.deferred` or unknown | Delivery uncertain | +| Offline stored | `undef.offline_stored` | `notification.endpoint.deferred` | Pending/unknown | +| Delivery receipt received | `success.delivered_to_client` | `notification.endpoint.accepted` | Strong client-delivery evidence | +| Displayed marker received | `success.displayed_by_client` | `interaction.notification.opened` | Strong display evidence | +| Acknowledged marker received | `success.acknowledged_by_client` | `interaction.acknowledgement_recorded` | Result evidence if policy accepts | +| Reply received | `success.reply_received` | `interaction.reply_received` | Strong awareness evidence | +| Authenticated JID interaction | strong | `interaction.authenticated_actor_interaction` | Stronger identity evidence | +| Stanza error permanent | `fail.stanza_error` | `notification.endpoint.rejected_permanent` | Strong endpoint failure | +| Stanza error temporary | `undef.deferred` | `notification.endpoint.rejected_temporary` | Retryable | +| MUC room message accepted | `undef.muc_room_message_participant_uncertain` | `notification.endpoint.accepted` | Room-level only | +| PubSub item published | `success.pubsub_item_published` | `feed.item.published` | Publication evidence | + +### 13.2 Coordination Undef Subclasses + +`coordination-engine` may derive these uncertainty classes from XMPP evidence: + +```text +undef.pending +undef.technical_acceptance_only +undef.endpoint_acceptance_only +undef.delivered_but_awareness_unproven +undef.displayed_but_comprehension_unproven +undef.identity_uncertain +undef.presence_available_but_no_receipt +undef.offline_delivery_unknown +undef.muc_participant_awareness_uncertain +undef.pubsub_publication_only +undef.conflicting_evidence +undef.channel_degraded +undef.delivery_pending +undef.escalation_required +``` + +XMPP evidence commonly produces: + +```text +undef.sent_to_server_only +undef.server_acknowledged_only +undef.receipt_requested_no_response +undef.delivered_but_awareness_unproven +undef.displayed_but_comprehension_unproven +undef.identity_uncertain +``` + +## 14. Evidence Grading Rules + +### 14.1 Server Acceptance / Stream Acknowledgement + +```yaml +event_type: notification.attempt.accepted_by_provider +evidence_grade: + strength: weak + actor_certainty: none + authority_certainty: none + payload_certainty: low + interaction_certainty: none + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: none +notes: + - XMPP server or stream accepted the stanza. + - Does not prove delivery to recipient client or human awareness. +``` + +### 14.2 Presence Available + +```yaml +event_type: notification.endpoint.accepted +evidence_grade: + strength: medium + actor_certainty: medium + authority_certainty: none + payload_certainty: none + interaction_certainty: low + timing_certainty: medium + channel_certainty: medium + non_repudiation_strength: low +notes: + - Recipient JID or resource appears available. + - Presence indicates possible reachability, not message display or action. +``` + +### 14.3 Delivery Receipt + +```yaml +event_type: notification.endpoint.accepted +evidence_grade: + strength: strong + actor_certainty: medium + authority_certainty: none + payload_certainty: medium + interaction_certainty: medium + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - XMPP delivery receipt indicates delivery to a client controlled by the intended recipient. + - Does not prove human reading, comprehension, or payload access. +``` + +### 14.4 Displayed Marker + +```yaml +event_type: interaction.notification.opened +evidence_grade: + strength: strong + actor_certainty: medium + authority_certainty: none + payload_certainty: medium + interaction_certainty: high + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Displayed marker indicates the client displayed the message. + - It is stronger awareness evidence than delivery receipt. + - It still does not prove comprehension or result satisfaction. +``` + +### 14.5 Acknowledgement Marker or ACK Reply + +```yaml +event_type: interaction.acknowledgement_recorded +evidence_grade: + strength: strong + actor_certainty: medium + authority_certainty: low + payload_certainty: medium + interaction_certainty: high + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - Client marker or explicit reply indicates acknowledgement-like interaction. + - Whether this satisfies the intended result is scenario-policy dependent. +``` + +### 14.6 Authenticated JID Reply + +```yaml +event_type: interaction.authenticated_actor_interaction +evidence_grade: + strength: strong + actor_certainty: high + authority_certainty: low + payload_certainty: medium + interaction_certainty: high + timing_certainty: high + channel_certainty: high + non_repudiation_strength: medium +notes: + - Reply came from an authenticated XMPP account or known resource. + - Authority still depends on participant and role modeling. +``` + +### 14.7 MUC Room Message Accepted + +```yaml +event_type: notification.endpoint.accepted +evidence_grade: + strength: medium + actor_certainty: low + authority_certainty: none + payload_certainty: medium + interaction_certainty: low + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - MUC room accepted or relayed the message. + - Does not prove that each intended participant saw it. +``` + +### 14.8 PubSub Item Published + +```yaml +event_type: feed.item.published +evidence_grade: + strength: medium + actor_certainty: none + authority_certainty: none + payload_certainty: medium + interaction_certainty: none + timing_certainty: high + channel_certainty: high + non_repudiation_strength: low +notes: + - XMPP PubSub item was published. + - Publication does not prove subscriber or human awareness. +``` + +## 15. Presence Model + +`xmpp-connect` SHOULD model presence as reachability context, not success. + +```yaml +XmppPresenceState: + endpoint_ref: EndpointRef + bare_jid: string + resource: string? + availability: available | unavailable | away | xa | dnd | chat | unknown + priority: integer? + status_text: string? + observed_at: timestamp + subscription_state: none | to | from | both | unknown + confidence: low | medium | high +``` + +Presence can influence policy: + +```text +if presence available: + prefer XMPP immediate notification +if presence unavailable: + send anyway with offline storage or use alternate channel +if do-not-disturb: + delay or use escalation policy +``` + +Presence MUST NOT be treated as proof of reading. + +## 16. Delivery Receipt and Marker Model + +```yaml +XmppDeliveryReceipt: + receipt_id: string + original_stanza_id: string + from_jid: string + to_jid: string + received_at: timestamp + resource: string? + confidence: low | medium | high +``` + +```yaml +XmppDisplayedMarker: + marker_id: string + original_stanza_id: string + marker_type: displayed | acknowledged | received | unknown + from_jid: string + to_jid: string + received_at: timestamp + resource: string? + confidence: low | medium | high +``` + +Receipts and markers should be correlated to original message IDs. + +If the marker uses cumulative semantics, the adapter SHOULD record that the marker applies to all relevant messages up to the marked message according to the extension semantics. + +## 17. Inbound Message and Command Model + +```yaml +XmppInboundMessage: + inbound_message_id: string + from_jid: XmppEndpoint + to_jid: XmppEndpoint + message_type: chat | normal | groupchat | headline | error | unknown + received_at: timestamp + body_text: string? + command_classification: none | acknowledgement | decline | help | free_text | unknown + correlation: CorrelationContext? + raw_stanza_ref: string? +``` + +Supported command classifications MAY include: + +```text +ACK +OK +CONFIRM +YES +NO +DECLINE +HELP +STATUS +STOP +UNSUBSCRIBE +``` + +`STOP`/unsubscribe semantics in XMPP are not as standardized as SMS. The adapter MAY map such messages to suppression only if scenario policy or adapter configuration enables it. + +## 18. MUC Model + +```yaml +XmppMucRoom: + room_jid: string + room_name: string? + room_type: public | members_only | password_protected | anonymous | semi_anonymous | unknown + occupant_mapping_confidence: low | medium | high + supports_history: boolean? + supports_markers: boolean? + metadata: object? +``` + +```yaml +XmppMucEvent: + room_jid: string + occupant_jid: string? + occupant_nick: string? + participant_id: string? + event_type: joined | left | message_sent | message_received | displayed | acknowledged | kicked | banned | error + occurred_at: timestamp + confidence: low | medium | high +``` + +MUC events require careful identity grading because room nicknames and occupant JIDs may not reliably identify real participants in all room configurations. + +## 19. PubSub Model + +```yaml +XmppPubSubNode: + service_jid: string + node_id: string + access_model: open | presence | roster | authorize | whitelist | unknown + publisher_model: publishers | subscribers | open | unknown + metadata: object? +``` + +```yaml +XmppPubSubItem: + item_id: string + service_jid: string + node_id: string + payload_ref: ResourceRef? + published_at: timestamp + publisher_jid: string? + correlation: CorrelationContext +``` + +PubSub should map primarily to feed/publication evidence unless subscriber-specific delivery evidence is available. + +## 20. Adapter Descriptor + +`xmpp-connect` MUST expose an `AdapterDescriptor` compatible with AdapterInterfaceSpecification.md v1.0. + +```yaml +adapter_id: xmpp-connect.default +adapter_name: xmpp-connect +adapter_version: 1.0.0 +adapter_contract_version: 1.0 +adapter_types: + - notification + - communication + - interaction + - feed +provider_family: xmpp +provider_name: configurable +deployment_mode: external +supported_channels: + - xmpp + - xmpp_muc + - xmpp_pubsub +supported_endpoint_types: + - bare_jid + - full_jid + - muc_room_jid + - pubsub_node + - component_jid +supported_actions: + - action_type: notification.send + mode: async + idempotency_required: true + - action_type: notification.send_reminder + mode: async + idempotency_required: true + - action_type: interaction.request_acknowledgement + mode: async + idempotency_required: true + - action_type: feed.publish_item + mode: async + idempotency_required: true + - action_type: notification.register_tracking_context + mode: sync + idempotency_required: true + - action_type: recipient.suppress + mode: sync + idempotency_required: true + - action_type: recipient.unsuppress + mode: sync + idempotency_required: true +emitted_event_types: + - notification.attempt.created + - notification.attempt.accepted_by_adapter + - notification.attempt.rejected_by_adapter + - notification.attempt.accepted_by_provider + - notification.endpoint.accepted + - notification.endpoint.deferred + - notification.endpoint.rejected_temporary + - notification.endpoint.rejected_permanent + - notification.endpoint.unreachable + - notification.endpoint.unknown + - interaction.notification.opened + - interaction.reply_received + - interaction.acknowledgement_recorded + - interaction.authenticated_actor_interaction + - interaction.unverified_actor_interaction + - identity.actor_authenticated + - feed.item.published + - feed.item.fetched + - system.provider.degraded + - system.provider.unavailable +evidence_profile: + strongest_evidence_level: medium_to_strong + can_prove_human_awareness: partially + can_prove_payload_delivery: false + can_prove_identity: partially +identity_profile: + identity_strength: medium_to_high + authority_strength: none_to_medium +limitations: + - Server acceptance does not prove client delivery. + - Stream management acknowledgement does not prove recipient awareness. + - Presence does not prove that a recipient read the message. + - Delivery receipts depend on client support and do not prove reading. + - Displayed markers provide stronger awareness evidence but not comprehension. + - Bare JID routing can create multi-resource ambiguity. + - MUC room delivery does not prove participant-specific awareness. + - PubSub publication does not prove subscriber or human awareness. +``` + +## 21. Action Request Handling + +### 21.1 `notification.send` + +`notification.send` sends a new XMPP message. + +Required fields: + +```text +request_id +action_type +coordination_case_id +participant_id +target_endpoint +content or template_ref +tracking_context +idempotency_key +requested_at +``` + +Example: + +```yaml +request_id: req_001 +action_type: notification.send +coordination_case_id: case_123 +participant_id: participant_456 +channel: xmpp +target_endpoint: + endpoint_type: bare_jid + value: recipient@example.net +template_ref: secure-document-xmpp-notification +variables: + portal_link: https://portal.example.com/access/abc +tracking_context: + correlation_id: corr_789 + coordination_case_id: case_123 + participant_id: participant_456 + notification_id: notif_001 + payload_id: payload_777 +idempotency_key: case_123:participant_456:xmpp:notif_001 +requested_at: 2026-01-01T12:00:00Z +``` + +### 21.2 Action Result + +The adapter returns: + +```yaml +request_id: req_001 +adapter_id: xmpp-connect.default +accepted: true +action_state: accepted +adapter_operation_id: xmppop_001 +initial_events: + - event_type: notification.attempt.accepted_by_adapter +received_at: 2026-01-01T12:00:01Z +``` + +The result does not prove that the XMPP message was delivered or read. + +## 22. Server / Provider Abstraction + +`xmpp-connect` SHOULD support a server/provider abstraction layer. + +Provider integration responsibilities: + +* open and maintain XMPP streams +* authenticate +* send stanzas +* request delivery receipts +* request/display markers where supported +* observe presence +* join or send to MUC rooms +* publish PubSub items +* ingest inbound messages +* normalize stanza errors +* expose server health + +Provider model: + +```yaml +XmppProvider: + provider_name: string + provider_account_ref: string + supported_features: + - client_to_server + - component_connection + - delivery_receipts + - displayed_markers + - stream_management + - chat_state_notifications + - message_archive_management + - multi_user_chat + - pubsub + - end_to_end_encryption + event_mapping_ref: string + configuration_ref: string +``` + +The first implementation MAY use a simulated XMPP server/client. Real implementations SHOULD be added behind this abstraction. + +## 23. Native XMPP Event Mapping + +The adapter MUST support XMPP-specific mapping modules. + +Common XMPP-native event groups: + +```text +stream_connected +stream_disconnected +stream_error +authenticated +authentication_failed +message_sent +message_error +presence_available +presence_unavailable +receipt_received +displayed_marker_received +acknowledged_marker_received +reply_received +muc_message_sent +muc_error +pubsub_item_published +pubsub_error +``` + +Important mapping rule: + +XMPP server acceptance or stream acknowledgement MUST NOT be interpreted as recipient client delivery. Delivery receipt and displayed marker evidence must be represented separately. + +## 24. Stanza Error Classification + +`xmpp-connect` SHOULD classify stanza errors. + +```yaml +XmppStanzaError: + error_type: cancel | continue | modify | auth | wait | unknown + condition: string + text: string? + retryable: boolean + classification: + - bad_request + - conflict + - feature_not_implemented + - forbidden + - gone + - internal_server_error + - item_not_found + - jid_malformed + - not_acceptable + - not_allowed + - not_authorized + - payment_required + - recipient_unavailable + - redirect + - registration_required + - remote_server_not_found + - remote_server_timeout + - resource_constraint + - service_unavailable + - subscription_required + - undefined_condition + - unexpected_request +``` + +Suggested mappings: + +| Error class | XMPP assessment | Normalized event | +| ----------------------- | -------------------------------- | ------------------------------------------ | +| jid_malformed | `fail.invalid_jid` | `notification.attempt.rejected_by_adapter` | +| item_not_found | `fail.recipient_not_found` | `notification.endpoint.rejected_permanent` | +| forbidden | `fail.authorization_failed` | `notification.endpoint.rejected_permanent` | +| not_authorized | `fail.authorization_failed` | `notification.endpoint.rejected_permanent` | +| recipient_unavailable | `undef.presence_unavailable` | `notification.endpoint.deferred` | +| remote_server_not_found | `fail.remote_server_unreachable` | `notification.endpoint.unreachable` | +| remote_server_timeout | `undef.deferred` | `notification.endpoint.rejected_temporary` | +| service_unavailable | `undef.deferred` or fail | `notification.endpoint.rejected_temporary` | +| resource_constraint | `undef.deferred` | `notification.endpoint.rejected_temporary` | +| feature_not_implemented | capability limitation | metadata or action rejection | + +## 25. Endpoint Quality + +`xmpp-connect` SHOULD maintain JID endpoint quality signals. + +```yaml +XmppEndpointQuality: + endpoint_ref: EndpointRef + jid_valid: boolean? + domain_resolves: boolean? + server_reachable: boolean? + roster_known: boolean? + subscription_state: none | to | from | both | unknown + last_presence_available_at: timestamp? + last_presence_unavailable_at: timestamp? + last_delivery_receipt_at: timestamp? + last_displayed_marker_at: timestamp? + last_reply_at: timestamp? + last_stanza_error_at: timestamp? + suppression_state: string? +``` + +Endpoint quality may be emitted as diagnostics but should not by itself create coordination success. + +## 26. Channel Health + +`xmpp-connect` SHOULD expose XMPP channel health. + +```yaml +XmppChannelHealth: + xmpp_account_ref: string + server_ref: string? + status: healthy | degraded | failing | unknown + stream_status: connected | disconnected | reconnecting | failed | unknown + authentication_status: authenticated | failed | expired | unknown + tls_status: active | failed | unavailable | unknown + server_reachability: healthy | degraded | unavailable | unknown + supported_extensions: + - string + known_degradations: + - string +``` + +Health-related normalized events: + +```text +system.provider.degraded +system.provider.unavailable +system.adapter.health_changed +``` + +## 27. Security Requirements + +`xmpp-connect` MUST: + +* protect XMPP credentials and tokens +* use TLS where required +* validate server identity where applicable +* preserve idempotency +* avoid duplicate sends for repeated idempotency keys +* avoid logging sensitive message content by default +* protect tracking and correlation IDs +* sanitize inbound messages before processing +* support tenant/account separation where applicable +* carefully model end-to-end encryption limitations + +If messages are end-to-end encrypted, the adapter may be unable to inspect content or some markers. This limitation MUST be declared in the descriptor or channel health metadata. + +## 28. Privacy Requirements + +`xmpp-connect` SHOULD: + +* store message content only when necessary +* support metadata-only mode +* support raw stanza redaction +* support configurable retention of raw stanzas +* store endpoint references instead of full JIDs where possible +* avoid unnecessary retention of presence history +* separate operational diagnostics from coordination evidence +* support deletion or anonymization workflows where applicable +* avoid overcollection of MUC occupant data + +## 29. Reliability Requirements + +`xmpp-connect` MUST support: + +* idempotent send requests +* duplicate stanza/event detection +* out-of-order event handling +* late receipt/marker handling +* reconnect logic +* stream interruption handling +* provider/server timeout handling +* correlation preservation +* dead-letter handling for unprocessable events +* explicit timeout/expiry handling for missing receipts + +Late events MUST be preserved. + +Example: + +```text +Message sent to server at 10:00 +No receipt by 10:05 +Participant unresolved at 10:30 +Delivery receipt arrives at 11:00 +``` + +The late receipt must still be recorded and emitted as evidence. + +## 30. Raw Event Preservation + +`xmpp-connect` SHOULD preserve raw stanzas or references to them. + +```yaml +RawXmppEventRef: + raw_event_id: string + source: stream | server | client | muc | pubsub | archive | operator + storage_ref: string? + received_at: timestamp + redacted: boolean +``` + +Normalized events should reference raw event data where available: + +```yaml +raw_event_ref: raw_xmpp_event_123 +``` + +## 31. Minimal API Surface + +`xmpp-connect` SHOULD expose a headless API. + +### 31.1 Adapter Contract API + +Required conceptual operations: + +```text +GET /adapter/descriptor +GET /adapter/health +POST /adapter/actions +POST /adapter/events/provider +GET /adapter/events +GET /adapter/messages/{id}/timeline +GET /adapter/messages/{id}/assessment +``` + +The actual transport may differ, but these conceptual operations should exist. + +### 31.2 Standalone API + +Useful standalone operations: + +```text +POST /xmpp/send +GET /xmpp/messages/{id} +GET /xmpp/messages/{id}/timeline +GET /xmpp/messages/{id}/assessment +GET /xmpp/endpoints/{id}/quality +GET /xmpp/presence/{jid} +POST /xmpp/muc/{room}/send +POST /xmpp/pubsub/{node}/publish +GET /xmpp/channel-health +``` + +## 32. Example End-to-End Flows + +### 32.1 Secure Document Notification + +1. `coordination-engine` creates a coordination case. +2. `portal-connect` creates an authenticated document action surface. +3. Policy selects XMPP for a participant with a known JID. +4. `coordination-engine` sends `notification.send` to `xmpp-connect`. +5. `xmpp-connect` sends an XMPP message requesting a delivery receipt. +6. XMPP server accepts the stanza. +7. `xmpp-connect` emits `notification.attempt.accepted_by_provider`. +8. Recipient client returns a delivery receipt. +9. `xmpp-connect` emits `notification.endpoint.accepted`. +10. Recipient clicks or opens the portal link. +11. `portal-connect` emits `identity.actor_authenticated`. +12. Recipient downloads document. +13. `portal-connect` emits `delivery.payload.downloaded`. +14. `coordination-engine` marks participant complete. + +### 32.2 Displayed Marker as Awareness Evidence + +1. XMPP message is sent with displayed marker request or marker-supporting context. +2. Recipient client displays the message. +3. `xmpp-connect` receives displayed marker. +4. `xmpp-connect` emits `interaction.notification.opened`. +5. If scenario policy accepts displayed marker as sufficient awareness evidence, `coordination-engine` marks notification awareness successful. +6. If payload access is required, participant remains incomplete until delivery evidence appears. + +### 32.3 XMPP ACK Reply + +1. Incident alert sent via XMPP. +2. Recipient replies `ACK`. +3. `xmpp-connect` emits `interaction.acknowledgement_recorded`. +4. If scenario policy accepts XMPP acknowledgement, `coordination-engine` marks participant acknowledged. +5. If stronger identity or authority is required, the case remains open until additional evidence is received. + +### 32.4 MUC Room Notification + +1. Incident notification is sent to an operations MUC room. +2. Room accepts the message. +3. `xmpp-connect` emits room-level `notification.endpoint.accepted`. +4. Specific occupants may later send displayed markers or replies. +5. `coordination-engine` treats the room event as group-channel evidence and occupant events as participant evidence only where occupant mapping is reliable. + +### 32.5 PubSub Publication + +1. `coordination-engine` requests `feed.publish_item`. +2. `xmpp-connect` publishes item to an XMPP PubSub node. +3. `xmpp-connect` emits `feed.item.published`. +4. Subscriber delivery evidence may follow if available. +5. `coordination-engine` treats publication as PubSub/feed evidence, not human awareness. + +## 33. Provider Implementation Guidance + +The first real implementation SHOULD be selected based on: + +* client library maturity +* XEP-0184 support +* XEP-0333 support if needed +* XEP-0198 support +* MUC support +* PubSub support +* message archive support +* server reliability +* webhook/event bridge options +* operational simplicity +* security model +* integration with existing identity systems + +The implementation SHOULD avoid hardcoding server-specific semantics into the core XMPP model. + +Implementation mapping should follow: + +```text +XMPP stanza / server event +→ XMPP-native event +→ XmppEvidenceAssessment +→ EvidenceEvent for coordination-engine +``` + +## 34. Channel Separation + +`xmpp-connect` SHOULD support separate XMPP accounts, components, rooms, or PubSub nodes for different purposes. + +Recommended streams: + +```text +transactional +notification +incident_alert +operations_room +machine_eventing +legal_or_high_assurance_notice +test +``` + +l separation may affect: + +* XMPP account +* component JID +* sender identity +* MUC room +* PubSub node +* roster and presence policy +* archive policy +* encryption policy +* retention policy +* participant mapping + +## 35. Legal and Compliance Disclaimer + +`xmpp-connect` does not by itself provide legal proof of delivery, legal notice, acceptance, signature, payment, or contract closure. + +It provides evidence from the XMPP channel. + +Scenario-specific applications and `coordination-engine` policies may combine XMPP evidence with stronger evidence from portal, identity, signature, payment, archive, or manual processes. + +The adapter MUST avoid naming technical XMPP events in ways that imply legal success. + +Use: + +```text +notification.endpoint.accepted +interaction.notification.opened +interaction.acknowledgement_recorded +``` + +Avoid: + +```text +recipient_legally_notified +human_read_confirmed +legal_delivery_completed +``` + +## 36. MVP Scope + +The first useful version of `xmpp-connect` should implement: + +1. Adapter descriptor. +2. Adapter health endpoint. +3. `notification.send`. +4. Idempotent send request handling. +5. Simulated XMPP provider or one real XMPP server/client. +6. XMPP message and attempt records. +7. XMPP stanza correlation. +8. Basic delivery receipt support. +9. Basic inbound reply support. +10. Basic presence observation. +11. Basic stanza error classification. +12. Evidence event generation. +13. Message timeline. +14. Message assessment. +15. Mapping to AdapterInterfaceSpecification.md v1.0. + +### MVP Required XMPP Events + +```text +notification.attempt.accepted_by_adapter +notification.attempt.rejected_by_adapter +notification.attempt.accepted_by_provider +notification.endpoint.accepted +notification.endpoint.deferred +notification.endpoint.rejected_temporary +notification.endpoint.rejected_permanent +notification.endpoint.unknown +interaction.reply_received +interaction.acknowledgement_recorded +interaction.authenticated_actor_interaction +interaction.unverified_actor_interaction +system.provider.degraded +system.provider.unavailable +``` + +### MVP Acceptance Criteria + +The MVP is acceptable when it can: + +1. Accept a coordination-compatible XMPP send request. +2. Dispatch or simulate an XMPP message. +3. Preserve correlation and idempotency. +4. Record server acceptance separately from recipient delivery. +5. Ingest or simulate delivery receipts. +6. Classify delivery receipt as strong client-delivery evidence but not human-awareness proof. +7. Record inbound replies as interaction evidence. +8. Record missing receipts as unresolved, not failure by default. +9. Provide a message timeline. +10. Provide an XMPP evidence assessment. +11. Integrate with coordination-engine without overclaiming success. + +## 37. Future Extensions + +Potential future capabilities: + +* XEP-0333 displayed marker support +* XEP-0198 stream management support +* XEP-0313 message archive integration +* XEP-0085 chat state notifications +* XEP-0045 Multi-User Chat support +* XEP-0060 PubSub support +* XMPP component mode +* OMEMO-aware evidence limitations +* roster and subscription management +* advanced participant-to-JID mapping +* MUC occupant identity resolution +* command bot integration +* escalation bot workflows +* natural-language reply classification +* bridge to Matrix/Slack/Mattermost adapters +* compliance archive integration +* signed stanza/event audit trail + +## 38. Non-Goals + +`xmpp-connect` is not: + +* a full chat application +* a general collaboration platform +* a workflow engine +* a legal notice system by itself +* a document portal +* a payment system +* a signature system +* a full identity provider +* the owner of coordination case success + +It may integrate with such systems or be used by them. + +## 39. Summary + +`xmpp-connect` models XMPP as a presence-aware, near-real-time notification, communication, interaction, group, and PubSub channel. + +Its job is to: + +* send XMPP messages +* observe presence and routeability +* request and ingest delivery receipts +* ingest displayed markers where supported +* record inbound replies and commands +* classify stanza and stream errors +* normalize XMPP evidence +* expose JID endpoint and channel diagnostics +* integrate cleanly with `coordination-engine` + +The key rule is: + +> XMPP events are evidence, not result satisfaction. xmpp-connect reports XMPP-channel facts and uncertainty. coordination-engine evaluates intended results. +