{
  "version": "1.0.0",
  "date": "2026-09-14",
  "synthetic": true,
  "method": "Original fictional records; expected outcomes independently authored from the specified invariants.",
  "cases": [
    {
      "id": "unchanged",
      "title": "Unchanged control",
      "purpose": "Valid records survive unchanged.",
      "expected": {
        "status": "compared",
        "afterIssueCodes": [],
        "summary": {
          "added": 0,
          "removed": 0,
          "changed": 0
        },
        "differences": []
      },
      "before": "migration-relational-before.json",
      "after": "migration-relational-unchanged-after.json"
    },
    {
      "id": "missing-job",
      "title": "Missing job",
      "purpose": "A valid dataset can still omit a required source record.",
      "expected": {
        "status": "compared",
        "afterIssueCodes": [],
        "summary": {
          "added": 0,
          "removed": 1,
          "changed": 0
        },
        "differences": [
          {
            "type": "removed",
            "table": "jobs",
            "id": "j2",
            "before": {
              "id": "j2",
              "userId": "u2",
              "purchaseId": "p2",
              "status": "queued"
            }
          }
        ]
      },
      "before": "migration-relational-before.json",
      "after": "migration-relational-missing-job-after.json"
    },
    {
      "id": "duplicate-id",
      "title": "Duplicate identifier",
      "purpose": "Equal counts conceal a duplicate and lost identity.",
      "expected": {
        "status": "unresolved",
        "afterIssueCodes": [
          "DUPLICATE_ID"
        ]
      },
      "before": "migration-relational-before.json",
      "after": "migration-relational-duplicate-id-after.json"
    },
    {
      "id": "broken-join",
      "title": "Broken user join",
      "purpose": "Equal counts conceal a purchase pointing to an absent user.",
      "expected": {
        "status": "unresolved",
        "afterIssueCodes": [
          "MISSING_USER",
          "MISSING_ENTITLEMENT",
          "JOB_OWNER_MISMATCH"
        ]
      },
      "before": "migration-relational-before.json",
      "after": "migration-relational-broken-join-after.json"
    },
    {
      "id": "job-owner",
      "title": "Job owner mismatch",
      "purpose": "Existing identifiers can still form the wrong relationship.",
      "expected": {
        "status": "unresolved",
        "afterIssueCodes": [
          "JOB_OWNER_MISMATCH"
        ]
      },
      "before": "migration-relational-before.json",
      "after": "migration-relational-job-owner-after.json"
    },
    {
      "id": "missing-entitlement",
      "title": "Progress without a purchase",
      "purpose": "A progress course must match a purchase for the same user.",
      "expected": {
        "status": "unresolved",
        "afterIssueCodes": [
          "MISSING_ENTITLEMENT"
        ]
      },
      "before": "migration-relational-before.json",
      "after": "migration-relational-missing-entitlement-after.json"
    },
    {
      "id": "changed-value",
      "title": "Changed price and progress",
      "purpose": "Exact numeric field differences remain visible.",
      "expected": {
        "status": "compared",
        "afterIssueCodes": [],
        "summary": {
          "added": 0,
          "removed": 0,
          "changed": 2
        },
        "differences": [
          {
            "type": "changed",
            "table": "purchases",
            "id": "p1",
            "field": "amountCents",
            "before": 1200,
            "after": 120
          },
          {
            "type": "changed",
            "table": "progress",
            "id": "g1",
            "field": "percent",
            "before": 25,
            "after": 0
          }
        ]
      },
      "before": "migration-relational-before.json",
      "after": "migration-relational-changed-value-after.json"
    },
    {
      "id": "equal-count-replacement",
      "title": "Equal-count record replacement",
      "purpose": "Two job totals match while one id disappears and another appears.",
      "expected": {
        "status": "compared",
        "afterIssueCodes": [],
        "summary": {
          "added": 1,
          "removed": 1,
          "changed": 0
        },
        "differences": [
          {
            "type": "removed",
            "table": "jobs",
            "id": "j2",
            "before": {
              "id": "j2",
              "userId": "u2",
              "purchaseId": "p2",
              "status": "queued"
            }
          },
          {
            "type": "added",
            "table": "jobs",
            "id": "j9",
            "after": {
              "id": "j9",
              "userId": "u2",
              "purchaseId": "p2",
              "status": "queued"
            }
          }
        ]
      },
      "before": "migration-relational-before.json",
      "after": "migration-relational-equal-count-replacement-after.json"
    }
  ]
}
