{
  "id": "FLOW-010",
  "name": "Quote AfterSave Update recordUpdate with cross-record assignments resolves correctly (#2228 regression lock)",
  "description": "Regression lock for #2228 (Quote_Account_Opportunity_field FAIL-SILENT). The original report observed: PATCH Quote → AfterSave-driven recordUpdate that writes 3 Quote target fields from $Record.Opportunity.Account.Name (2-hop), $Record.Opportunity.Account.Owner.Username (3-hop), and $Record.Opportunity.Name (1-hop) → all 3 target fields stayed NULL. Investigation showed the engine resolves multi-hop standard relationships correctly (resolveNestedReferenceCtx + loadRelatedRecord); the original verifier read at sleep(1.0s) before the AfterSave outbox dispatcher had picked up the event. This case mirrors the issue acceptance criteria: seed Account+Opportunity+Quote, install a synthetic Quote AfterSave Update flow whose recordUpdate uses implicit-Id (Id == $Record.Id) and writes 3 standard Quote text fields from the same 1-hop / 2-hop / 3-hop $Record.Opportunity.<...> assignments, PATCH the Quote to fire AfterSave on Update, then poll until all 3 fields populate. Uses Owner.Email rather than Owner.Username for the 3-hop because the e2e-api runner's admin bootstrap stores Username='admin' and Email='admin@test.com' (tests/e2e-api/runner.py:740, conftest.py:56) — Email is the stable cross-environment value. Tenant-agnostic: standard SObjects (Account, Opportunity, Quote, User), standard fields (Name, AccountId, OpportunityId, OwnerId, Email, Description, BillingStreet, ShippingStreet, Phone, StageName, CloseDate). Companion to FLOW-006 (BeforeSave cross-record nil-skip) and FLOW-007 (AfterSave implicit-Id null semantics).",
  "object": "Quote",
  "method": "PATCH",
  "endpoint": "/services/data/v63.0/sobjects/Quote/{flow010_quote_id}",
  "setup": [
    {
      "type": "http",
      "name": "Create test Account (root of the cross-record chain; Owner inherits from caller=admin)",
      "method": "POST",
      "endpoint": "/services/data/v63.0/sobjects/Account",
      "payload": {
        "Name": "FLOW-010-CrossRecord-Account"
      },
      "expects": {
        "status_code": 201
      },
      "extract": {
        "flow010_account_id": "id"
      }
    },
    {
      "type": "http",
      "name": "Create test Opportunity linked to Account (middle hop in Quote → Opportunity → Account → Owner chain)",
      "method": "POST",
      "endpoint": "/services/data/v63.0/sobjects/Opportunity",
      "payload": {
        "Name": "FLOW-010-CrossRecord-Opp",
        "AccountId": "{flow010_account_id}",
        "StageName": "Prospecting",
        "CloseDate": "2026-12-31"
      },
      "expects": {
        "status_code": 201
      },
      "extract": {
        "flow010_opp_id": "id"
      }
    },
    {
      "type": "http",
      "name": "Create test Quote linked to Opportunity (BEFORE installing the flow, so initial create does not fire AfterSave)",
      "method": "POST",
      "endpoint": "/services/data/v63.0/sobjects/Quote",
      "payload": {
        "Name": "FLOW-010-CrossRecord-Quote",
        "OpportunityId": "{flow010_opp_id}"
      },
      "expects": {
        "status_code": 201
      },
      "extract": {
        "flow010_quote_id": "id"
      }
    },
    {
      "type": "http",
      "name": "Install synthetic Flow: Quote AfterSave Update → recordUpdate with implicit-Id writes 3 cross-record assignments",
      "method": "POST",
      "endpoint": "/services/data/v63.0/tooling/sobjects/Flow",
      "payload": {
        "MasterLabel": "FLOW-010 Quote AfterSave CrossRecord",
        "ProcessType": "AutoLaunchedFlow",
        "Status": "Draft",
        "Metadata": {
          "apiVersion": "63.0",
          "label": "FLOW-010 Quote AfterSave CrossRecord",
          "description": "Test-only Quote AfterSave Update flow that mirrors #2228's reported shape: implicit-Id recordUpdate writes 3 Quote target fields from cross-record traversals — $Record.Opportunity.Name (1-hop) → Description, $Record.Opportunity.Account.Name (2-hop) → BillingStreet, $Record.Opportunity.Account.Owner.Email (3-hop) → ShippingStreet. Regression lock for #2228 — the original Quote_Account_Opportunity_field FAIL-SILENT had structurally identical assignments. Teardown deletes this flow version.",
          "status": "Draft",
          "processType": "AutoLaunchedFlow",
          "start": {
            "locationX": 50,
            "locationY": 0,
            "object": "Quote",
            "triggerType": "RecordAfterSave",
            "recordTriggerType": "Update",
            "connector": {
              "targetReference": "WriteCrossRecordAssignmentsToQuote"
            }
          },
          "recordUpdates": [
            {
              "name": "WriteCrossRecordAssignmentsToQuote",
              "label": "Write Cross-Record Assignments to Quote",
              "locationX": 176,
              "locationY": 287,
              "object": "Quote",
              "filterLogic": "and",
              "filters": [
                {
                  "field": "Id",
                  "operator": "EqualTo",
                  "processMetadataValues": [
                    {
                      "name": "implicit",
                      "value": {
                        "booleanValue": true
                      }
                    }
                  ],
                  "value": {
                    "elementReference": "$Record.Id"
                  }
                }
              ],
              "inputAssignments": [
                {
                  "field": "Description",
                  "value": {
                    "elementReference": "$Record.Opportunity.Name"
                  }
                },
                {
                  "field": "BillingStreet",
                  "value": {
                    "elementReference": "$Record.Opportunity.Account.Name"
                  }
                },
                {
                  "field": "ShippingStreet",
                  "value": {
                    "elementReference": "$Record.Opportunity.Account.Owner.Email"
                  }
                }
              ]
            }
          ]
        }
      },
      "expects": {
        "status_code": 201
      },
      "extract": {
        "flow010_version_id": "id"
      }
    },
    {
      "type": "http",
      "name": "Activate the test Flow so it fires on Quote AfterSave Update",
      "method": "POST",
      "endpoint": "/services/data/v63.0/tooling/sobjects/Flow/{flow010_version_id}/activate",
      "expects": {
        "status_code": 204
      }
    }
  ],
  "payload": {
    "Phone": "+1-555-flow010-trigger"
  },
  "expects": {
    "status_code": 204
  },
  "query_after": {
    "object": "Quote",
    "record_id": "{flow010_quote_id}",
    "retry": 15,
    "delay_ms": 2000,
    "assertions": [
      {
        "field": "Description",
        "operator": "eq",
        "value": "FLOW-010-CrossRecord-Opp"
      },
      {
        "field": "BillingStreet",
        "operator": "eq",
        "value": "FLOW-010-CrossRecord-Account"
      },
      {
        "field": "ShippingStreet",
        "operator": "eq",
        "value": "admin@test.com"
      }
    ]
  },
  "teardown": [
    {
      "method": "DELETE",
      "endpoint": "/services/data/v63.0/sobjects/Quote/{flow010_quote_id}"
    },
    {
      "method": "DELETE",
      "endpoint": "/services/data/v63.0/sobjects/Opportunity/{flow010_opp_id}"
    },
    {
      "method": "DELETE",
      "endpoint": "/services/data/v63.0/sobjects/Account/{flow010_account_id}"
    },
    {
      "method": "DELETE",
      "endpoint": "/services/data/v63.0/tooling/sobjects/Flow/{flow010_version_id}"
    }
  ],
  "priority": "P1",
  "bug_ref": "#2228",
  "scene": "flow"
}
