{
  "id": "k-assoc-01",
  "title": "Converted Lead's Opportunity carries an Account and outbounds a deal->company association (deal->contact assoc gap = #2851)",
  "kind": "hubspot_sync",
  "executionMode": "serial",
  "executionModeReason": "HubSpot contact materialization is queue-sensitive under the full-suite webhook burst and must run without competing case traffic.",
  "linkedIssues": [
    2851
  ],
  "caseTimeoutMs": 540000,
  "provenance": {
    "businessScenario": "K-ASSOC-01 — association consistency on the convert path: an INBOUND-origin Lead (HubSpot contact -> NX Lead) is converted via convertLead into an Opportunity against a pre-created Account; the Opportunity must carry AccountId and outbound a HubSpot deal whose deal->COMPANY association lands (count atLeast 1). Reproduces #2851: the deal->CONTACT association does NOT land after convert. This case asserts ONLY what holds on ksyun (Opportunity has AccountId + deal exists + deal->company assoc atLeast 1) so it PASSes as behavior-observed; deal->contact is intentionally NOT asserted (real open defect would FAIL).",
    "reportedBy": "HubSpot sync spec K-ASSOC-01 (hubspot-nexuscrm-sync-CASE-EXEC-SELFCONTAINED)",
    "capturedAt": "2026-06-18",
    "calibrated": "[recalibrated 2026-06-24] Added wait-account-sync: poll the Account's HubSpot_Id__c (Account outbound completed) BEFORE convert. This mirrors the real business flow where the Account is created and approved/synced first, then the lead is converted after a time gap — by then the Account already has a HubSpot company id. Previously the case converted immediately after creating the Account, so the Opp's RecordAfterSave deal outbound (which inlines a deal->company association on AccountId -> Account.HubSpot_Id__c) often fired before the Account synced, the association could not resolve, and the deal create failed with no retry -> Opp.HubSpot_Id__c stayed null (step opp assertion 2 failed). Gating on Account sync makes the case deterministic and faithful. Bumped caseTimeoutMs to 540000 because adding a 90s step pushes the summed step poll budgets to 450s, above the global 420000 default. wait-account-sync onTimeout=blocked: if the Account itself never outbounds, that is an Account-outbound throughput precondition, not a k-assoc-01 failure. [calibrated] reworked from the inbound v4-association direction to the verified convertLead path (POST /actions/standard/convertLead, convertedStatus 'Open Opportunity', pre-created Account, inbound-origin lead). Asserts Opp.AccountId + deal + deal->company assoc atLeast 1; deal->contact documented as the #2851 gap and unasserted.",
    "knownDefect": "#2851 — converted Opportunity's outbound deal carries deal->company but NOT deal->contact association; deal->contact intentionally unasserted."
  },
  "steps": [
    {
      "id": "account-record-type",
      "system": "nexuscrm",
      "authRole": "admin",
      "method": "GET",
      "path": "/services/data/v63.0/query?q=SELECT Id FROM _System_RecordType WHERE SobjectType = 'Account' AND Name = 'Customer' AND IsActive = true LIMIT 1",
      "expectStatus": 200,
      "assertions": [
        {
          "path": "response.body.totalSize",
          "atLeast": 1
        },
        {
          "path": "response.body.records[0].Id",
          "exists": true
        }
      ]
    },
    {
      "id": "acct",
      "system": "nexuscrm",
      "authRole": "admin",
      "method": "POST",
      "path": "/services/data/v63.0/sobjects/Account",
      "body": {
        "Name": "AssocAcct-{{__runId}}",
        "Industry": "FSI-FinTech",
        "NumberOfEmployees": 120,
        "RecordTypeId": "{{steps.account-record-type.response.body.records[0].Id}}",
        "Region__c": "NA",
        "Vertical__c": "Payment",
        "Website": "https://kassoc-{{__runId}}.example.com"
      },
      "expectStatusRange": [
        200,
        299
      ],
      "assertions": [
        {
          "path": "response.body.id",
          "exists": true
        }
      ]
    },
    {
      "id": "wait-account-sync",
      "system": "nexuscrm",
      "authRole": "admin",
      "method": "GET",
      "path": "/services/data/v63.0/query?q=SELECT HubSpot_Id__c FROM Account WHERE Id='{{steps.acct.response.body.id}}' LIMIT 1",
      "expectStatus": 200,
      "poll": {
        "budgetMs": 90000,
        "intervalMs": 6000
      },
      "onTimeout": "blocked",
      "assertions": [
        {
          "path": "response.body.records[0].HubSpot_Id__c",
          "exists": true
        }
      ]
    },
    {
      "id": "hsContact",
      "system": "hubspot",
      "method": "POST",
      "path": "/crm/v3/objects/contacts",
      "body": {
        "properties": {
          "email": "kassoc-{{__runId}}@example.com",
          "firstname": "A",
          "lastname": "AssocCt{{__runId}}",
          "company": "AssocCo-{{__runId}}"
        }
      },
      "expectStatusRange": [
        200,
        299
      ],
      "assertions": [
        {
          "path": "response.body.id",
          "exists": true
        }
      ]
    },
    {
      "id": "lead",
      "system": "nexuscrm",
      "authRole": "admin",
      "method": "GET",
      "path": "/services/data/v63.0/query?q=SELECT Id,HubSpot_Id__c FROM Lead WHERE HubSpot_Id__c='{{steps.hsContact.response.body.id}}'",
      "expectStatus": 200,
      "poll": {
        "budgetMs": 90000,
        "intervalMs": 6000
      },
      "onTimeout": "fail",
      "assertions": [
        {
          "path": "response.body.records[0].Id",
          "exists": true
        }
      ]
    },
    {
      "id": "convert",
      "system": "nexuscrm",
      "authRole": "admin",
      "method": "POST",
      "path": "/services/data/v63.0/actions/standard/convertLead",
      "body": {
        "inputs": [
          {
            "leadId": "{{steps.lead.response.body.records[0].Id}}",
            "convertedStatus": "Open Opportunity",
            "accountId": "{{steps.acct.response.body.id}}"
          }
        ]
      },
      "expectStatusRange": [
        200,
        299
      ],
      "onTimeout": "fail",
      "assertions": [
        {
          "path": "response.body[0].isSuccess",
          "equals": true
        },
        {
          "path": "response.body[0].outputValues.opportunityId",
          "exists": true
        }
      ]
    },
    {
      "id": "opp",
      "system": "nexuscrm",
      "authRole": "admin",
      "method": "GET",
      "path": "/services/data/v63.0/query?q=SELECT Id,AccountId,HubSpot_Id__c FROM Opportunity WHERE Id='{{steps.convert.response.body[0].outputValues.opportunityId}}'",
      "expectStatus": 200,
      "poll": {
        "budgetMs": 90000,
        "intervalMs": 6000
      },
      "onTimeout": "fail",
      "assertions": [
        {
          "path": "response.body.records[0].AccountId",
          "exists": true
        },
        {
          "path": "response.body.records[0].HubSpot_Id__c",
          "exists": true
        }
      ]
    },
    {
      "id": "deal",
      "system": "hubspot",
      "method": "GET",
      "path": "/crm/v3/objects/deals/{{steps.opp.response.body.records[0].HubSpot_Id__c}}",
      "expectStatusRange": [200, 299],
      "poll": {
        "budgetMs": 90000,
        "intervalMs": 6000
      },
      "onTimeout": "fail",
      "assertions": [
        {
          "path": "response.body.id",
          "exists": true
        }
      ]
    },
    {
      "id": "assocCo",
      "system": "hubspot",
      "method": "GET",
      "path": "/crm/v4/objects/deals/{{steps.opp.response.body.records[0].HubSpot_Id__c}}/associations/companies",
      "expectStatusRange": [200, 299],
      "poll": {
        "budgetMs": 90000,
        "intervalMs": 6000
      },
      "onTimeout": "fail",
      "assertions": [
        {
          "path": "response.body.results",
          "lengthAtLeast": 1
        }
      ]
    }
  ]
}
