{
  "id": "out-contact",
  "title": "Converted Contact Title PATCH contract stays admitted and serialized as HubSpot jobtitle (#2837)",
  "kind": "hubspot_contract",
  "linkedIssues": [2837, 2830],
  "externalEffects": "none",
  "provenance": {
    "businessScenario": "After a converted Contact commits, the companion-owned HubSpot flow must move an identity only from its exact converted Lead, then serialize Contact.Title as the HubSpot jobtitle PATCH property. This is intentionally a pack contract inspection: Xiaopeng's database credential is not an isolated HubSpot test credential, so this case must not create, update, query, or delete a HubSpot record, and must not trigger a CRM outbound worker.",
    "acceptanceBoundary": "PASS proves the committed companion after-commit handoff, outbound action, and mapping contract. It does not prove scheduler execution or a live provider delivery; those require a deployed runtime and a separately isolated connector endpoint."
  },
  "contractChecks": [
    {
      "id": "converted-contact-handoff-is-scheduled-after-commit",
      "type": "flow_start",
      "pack": "PingCAP-HubSpot-Companion",
      "flow": "HubSpot_Contact_Converted_Lead_Identity_Handoff.flow.json",
      "expect": {
        "runAtomically": true,
        "object": "Contact",
        "triggerType": "RecordAfterSave",
        "recordTriggerType": "CreateAndUpdate",
        "filters": [
          { "field": "HubSpot_Id__c", "operator": "IsNotBlank" }
        ],
        "scheduledPaths": [
          {
            "name": "AsyncAfterCommit",
            "label": "Hand off finalized converted Lead identity after Contact commit",
            "pathType": "AsyncAfterCommit",
            "connector": { "targetReference": "SkipHubSpotConnectorInbound" }
          }
        ]
      }
    },
    {
      "id": "companion-resolves-its-owned-hubspot-contact-identity",
      "type": "flow_action",
      "pack": "PingCAP-HubSpot-Companion",
      "flow": "HubSpot_Contact_Converted_Lead_Identity_Handoff.flow.json",
      "action": "ResolveContactRemoteIdentity",
      "expect": {
        "actionName": "resolveConnectorRemoteIdentity",
        "actionType": "resolveConnectorRemoteIdentity",
        "inputParameters": {
          "connectorOwnerKey": { "stringValue": "pingcap-hubspot-companion" },
          "destinationSlotId": { "stringValue": "hubspot-contact" },
          "remoteId": { "elementReference": "$Record.HubSpot_Id__c" }
        },
        "outputParameters": {
          "ownerLifecycle": { "elementReference": "durableOwnerLifecycle" }
        }
      }
    },
    {
      "id": "finalized-identity-must-belong-to-this-converted-contact-lead",
      "type": "flow_rule",
      "pack": "PingCAP-HubSpot-Companion",
      "flow": "HubSpot_Contact_Converted_Lead_Identity_Handoff.flow.json",
      "decision": "RouteAuthoritativeConvertedLead",
      "rule": "FinalizedLeadAuthority",
      "expect": {
        "conditions": [
          { "leftValueReference": "durableAuthorityState", "operator": "EqualTo", "rightValue": { "stringValue": "finalized" } },
          { "leftValueReference": "durableSourceObject", "operator": "EqualTo", "rightValue": { "stringValue": "Lead" } },
          { "leftValueReference": "durableOwnerLifecycle", "operator": "EqualTo", "rightValue": { "stringValue": "active" } },
          { "leftValueReference": "durableSourceRecordId", "operator": "IsNotBlank" }
        ],
        "connector": { "targetReference": "FindConvertedLeadForContactHandoff" }
      }
    },
    {
      "id": "identity-transfer-looks-up-the-exact-converted-lead",
      "type": "record_lookup",
      "pack": "PingCAP-HubSpot-Companion",
      "flow": "HubSpot_Contact_Converted_Lead_Identity_Handoff.flow.json",
      "lookup": "FindConvertedLeadForContactHandoff",
      "expect": {
        "object": "Lead",
        "filters": [
          { "field": "Id", "operator": "EqualTo", "value": { "elementReference": "durableSourceRecordId" } },
          { "field": "IsConverted", "operator": "EqualTo", "value": true },
          { "field": "ConvertedContactId", "operator": "EqualTo", "value": { "elementReference": "$Record.Id" } },
          { "field": "HubSpot_Id__c", "operator": "EqualTo", "value": { "elementReference": "$Record.HubSpot_Id__c" } }
        ]
      }
    },
    {
      "id": "identity-transfer-is-exact-lead-to-contact",
      "type": "flow_action",
      "pack": "PingCAP-HubSpot-Companion",
      "flow": "HubSpot_Contact_Converted_Lead_Identity_Handoff.flow.json",
      "action": "TransferConvertedLeadRemoteIdentityToContact",
      "expect": {
        "actionName": "transferConnectorRemoteIdentity",
        "actionType": "transferConnectorRemoteIdentity",
        "inputParameters": {
          "sourceObject": { "stringValue": "Lead" },
          "sourceId": { "elementReference": "$convertedLeadForContactHandoff.Id" },
          "targetObject": { "stringValue": "Contact" },
          "targetId": { "elementReference": "$Record.Id" },
          "remoteIDField": { "stringValue": "HubSpot_Id__c" },
          "outputReference": { "stringValue": "convertedLeadRemoteIdentityTransferred" }
        },
        "connector": { "targetReference": "ConfirmConvertedLeadIdentityHandoff" }
      }
    },
    {
      "id": "contact-title-uses-existing-contact-patch-contract",
      "type": "flow_action",
      "flow": "HubSpot_Contact_Contact_Patch.flow.json",
      "action": "PatchHubSpotContact",
      "expect": {
        "actionName": "PushToConnector",
        "actionType": "PushToConnector",
        "inputParameters": {
          "profileOwnerPack": { "stringValue": "hubspot-crm-connector" },
          "profileName": { "stringValue": "hubspot-crm-contact-write" },
          "sourceObject": { "stringValue": "Contact" },
          "targetObject": { "stringValue": "contacts" },
          "remoteId": { "elementReference": "$Record.HubSpot_Id__c" },
          "updatePathTemplate": { "stringValue": "/crm/v3/objects/contacts/{remoteId}" }
        }
      }
    },
    {
      "id": "contact-title-serializes-as-jobtitle",
      "type": "mapping_row",
      "mapping": "contact_contact_outbound.json",
      "rule": { "sourceObject": "Contact", "targetObject": "contacts", "actionType": "PushToConnectorDeltaTitle", "direction": "outbound" },
      "row": { "sourceField": "Title", "targetField": "jobtitle" },
      "expect": { "sourceField": "Title", "targetField": "jobtitle" }
    },
    {
      "id": "contact-owner-shadow-serializes-as-hubspot-owner-id",
      "type": "mapping_row",
      "mapping": "contact_contact_outbound.json",
      "rule": { "sourceObject": "Contact", "targetObject": "contacts", "actionType": "PushToConnector", "direction": "outbound" },
      "row": { "sourceField": "HubSpot_Owner_Id__c", "targetField": "hubspot_owner_id" },
      "expect": { "sourceField": "HubSpot_Owner_Id__c", "targetField": "hubspot_owner_id" }
    }
  ]
}
