{
  "id": "FLOW-012",
  "name": "Sibling RecordBeforeSave flows on same SObject both fire on single PATCH (issue #2254 re-entrancy granularity)",
  "description": "Regression lock for #2254: the FlowTriggerListener re-entrancy guard previously keyed on (object, recordID), which short-circuited ALL further record-triggered flows on the same record once any was executing — starving legitimate sibling flows. Salesforce semantics require all matching record-triggered flows to run on a single DML; recursion is per-flow, not per-record. This case installs two independent BeforeSave AutoLaunchedFlows on Opportunity, each writing a distinct standard field (Description and NextStep), then performs a single PATCH and asserts BOTH assignments persisted. Tenant-agnostic: standard SObject (Opportunity), standard fields, no __c business names.",
  "setup": [
    {
      "type": "http",
      "name": "Create sibling flow A — sets Description",
      "method": "POST",
      "endpoint": "/services/data/v63.0/tooling/sobjects/Flow",
      "payload": {
        "MasterLabel": "FLOW-012 Opp BeforeSave A Description",
        "ProcessType": "AutoLaunchedFlow",
        "Status": "Draft",
        "Metadata": {
          "apiVersion": "63.0",
          "label": "FLOW-012 Opp BeforeSave A Description",
          "description": "Test-only BeforeSave flow A. Regression lock for #2254 sibling-flow starvation.",
          "status": "Draft",
          "processType": "AutoLaunchedFlow",
          "start": {
            "locationX": 50,
            "locationY": 0,
            "object": "Opportunity",
            "triggerType": "RecordBeforeSave",
            "recordTriggerType": "Update",
            "connector": {
              "targetReference": "Set_Description"
            }
          },
          "assignments": [
            {
              "name": "Set_Description",
              "label": "Set Description",
              "locationX": 176,
              "locationY": 287,
              "assignmentItems": [
                {
                  "assignToReference": "$Record.Description",
                  "operator": "Assign",
                  "value": {
                    "stringValue": "FLOW-012-A-FIRED"
                  }
                }
              ]
            }
          ]
        }
      },
      "expects": {
        "status_code": 201
      },
      "extract": {
        "flow012a_version_id": "id"
      }
    },
    {
      "type": "http",
      "name": "Activate sibling flow A",
      "method": "POST",
      "endpoint": "/services/data/v63.0/tooling/sobjects/Flow/{flow012a_version_id}/activate",
      "expects": {
        "status_code": 204
      }
    },
    {
      "type": "http",
      "name": "Create sibling flow B — sets NextStep",
      "method": "POST",
      "endpoint": "/services/data/v63.0/tooling/sobjects/Flow",
      "payload": {
        "MasterLabel": "FLOW-012 Opp BeforeSave B NextStep",
        "ProcessType": "AutoLaunchedFlow",
        "Status": "Draft",
        "Metadata": {
          "apiVersion": "63.0",
          "label": "FLOW-012 Opp BeforeSave B NextStep",
          "description": "Test-only BeforeSave flow B. Independent of flow A; both must fire on a single PATCH.",
          "status": "Draft",
          "processType": "AutoLaunchedFlow",
          "start": {
            "locationX": 50,
            "locationY": 0,
            "object": "Opportunity",
            "triggerType": "RecordBeforeSave",
            "recordTriggerType": "Update",
            "connector": {
              "targetReference": "Set_NextStep"
            }
          },
          "assignments": [
            {
              "name": "Set_NextStep",
              "label": "Set NextStep",
              "locationX": 176,
              "locationY": 287,
              "assignmentItems": [
                {
                  "assignToReference": "$Record.NextStep",
                  "operator": "Assign",
                  "value": {
                    "stringValue": "FLOW-012-B-FIRED"
                  }
                }
              ]
            }
          ]
        }
      },
      "expects": {
        "status_code": 201
      },
      "extract": {
        "flow012b_version_id": "id"
      }
    },
    {
      "type": "http",
      "name": "Activate sibling flow B",
      "method": "POST",
      "endpoint": "/services/data/v63.0/tooling/sobjects/Flow/{flow012b_version_id}/activate",
      "expects": {
        "status_code": 204
      }
    },
    {
      "type": "http",
      "name": "Create the Opportunity that will be PATCHed",
      "method": "POST",
      "endpoint": "/services/data/v63.0/sobjects/Opportunity",
      "payload": {
        "Name": "FLOW-012-Sibling-Flows",
        "AccountId": "{seed_Account_0}",
        "CloseDate": "2026-12-31",
        "StageName": "Prospecting"
      },
      "expects": {
        "status_code": 201
      },
      "extract": {
        "opp_id": "id"
      }
    }
  ],
  "object": "Opportunity",
  "method": "PATCH",
  "endpoint": "/services/data/v63.0/sobjects/Opportunity/{opp_id}",
  "payload": {
    "StageName": "Negotiation"
  },
  "expects": {
    "status_code": 204
  },
  "query_after": {
    "object": "Opportunity",
    "record_id": "{opp_id}",
    "retry": 5,
    "delay_ms": 500,
    "assertions": [
      {
        "field": "Description",
        "operator": "eq",
        "value": "FLOW-012-A-FIRED"
      },
      {
        "field": "NextStep",
        "operator": "eq",
        "value": "FLOW-012-B-FIRED"
      },
      {
        "field": "StageName",
        "operator": "eq",
        "value": "Negotiation"
      }
    ]
  },
  "teardown": [
    {
      "method": "DELETE",
      "endpoint": "/services/data/v63.0/sobjects/Opportunity/{opp_id}"
    },
    {
      "method": "DELETE",
      "endpoint": "/services/data/v63.0/tooling/sobjects/Flow/{flow012a_version_id}"
    },
    {
      "method": "DELETE",
      "endpoint": "/services/data/v63.0/tooling/sobjects/Flow/{flow012b_version_id}"
    }
  ],
  "priority": "P1",
  "bug_ref": "#2254",
  "scene": "flow"
}
