{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://schemas.example.com/prw/queue/product.accepted-change.v1.json",
  "title": "AcceptedChangeEventV1",
  "description": "Accepted-change/1. Resolver outbox to product inbox, at least once with no arrival ordering guarantee. Accepted-change committed positions are contiguous within (tenant_id, catalog_id, history_epoch). Writers allocate them with state and outbox under the transactional cursor lock; rollback consumes no position. Queue arrivals may be unordered, duplicated or temporarily missing, so consumers park dependencies and retrieve retained events to close gaps. Durable progress advances only through every processed position, including safely ignored stale revisions. A DLQ entry or an ACK of a parked event does not close a gap. Internal resolution/replay jobs use their own dedupe IDs and do not share this committed cursor. Commit projection changes, inbox outcomes, dependency parking and the greatest contiguous processed position together in the live product transaction. Persist/report progress monotonically to resolver retention control for the same tenant, catalog and history epoch only after that commit; an interrupted report conservatively retains extra events. The pruner may use only acknowledged durable progress. Shadow catch-up has a separate cursor and lease; it never advances the live cursor. Cutover atomically swaps serving generation and durable live cursor before releasing the rebuild lease. Payloads contain accepted facts and immutable refs, never raw artifacts. Maximum modeled payload: 64 KB.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "event_id",
    "tenant_id",
    "catalog_id",
    "sequence",
    "type",
    "schema_version",
    "aggregate",
    "dependencies",
    "decision_id",
    "evidence",
    "observed_at",
    "accepted_at",
    "published_at",
    "payload",
    "history_epoch"
  ],
  "properties": {
    "event_id": {
      "type": "string",
      "pattern": "^evt_[A-Za-z0-9_]+$",
      "description": "Stable across redelivery."
    },
    "tenant_id": {
      "type": "string",
      "pattern": "^ten_[a-z0-9_]+$",
      "description": "PROP · stamped from the outbox row."
    },
    "catalog_id": {
      "type": "string",
      "pattern": "^cat_[a-z0-9_]+$",
      "description": "PROP · cat_primary or a tenant overlay such as cat_primary."
    },
    "sequence": {
      "type": "integer",
      "minimum": 1,
      "description": "Transactional committed cursor position under per-stream writer row lock; no PostgreSQL SEQUENCE."
    },
    "type": {
      "type": "string",
      "examples": [
        "listing_mapping.corrected",
        "catalog_item.created",
        "shipping_policy.revised",
        "inventory_observation.recorded"
      ]
    },
    "schema_version": {
      "const": "accepted-change/1"
    },
    "aggregate": {
      "$ref": "#/$defs/AggregateRef"
    },
    "dependencies": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/AggregateRef"
      }
    },
    "decision_id": {
      "type": "string",
      "pattern": "^dec_[A-Za-z0-9_]+$"
    },
    "evidence": {
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^cap_[A-Za-z0-9_]+$"
      },
      "description": "Capture ids only."
    },
    "observed_at": {
      "type": "string",
      "format": "date-time"
    },
    "accepted_at": {
      "type": "string",
      "format": "date-time"
    },
    "published_at": {
      "type": "string",
      "format": "date-time"
    },
    "payload": {
      "type": "object"
    },
    "history_epoch": {
      "type": "string",
      "minLength": 1,
      "description": "External restore fence. Consumer rejects old/unknown epoch before processing; never relabel old deliveries."
    }
  },
  "$defs": {
    "AggregateRef": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "type",
        "id",
        "revision"
      ],
      "properties": {
        "type": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "revision": {
          "type": "integer",
          "minimum": 1
        }
      }
    }
  },
  "examples": [
    {
      "event_id": "evt_0142",
      "tenant_id": "ten_primary",
      "catalog_id": "cat_primary",
      "sequence": 9,
      "type": "listing_mapping.corrected",
      "schema_version": "accepted-change/1",
      "aggregate": {
        "type": "listing_mapping",
        "id": "lst_lum_p5_1t_hs",
        "revision": 2
      },
      "dependencies": [
        {
          "type": "catalog_item",
          "id": "itm_ssd_p5_1t_hs",
          "revision": 1
        }
      ],
      "decision_id": "dec_0141",
      "evidence": [
        "cap_lum_0310"
      ],
      "observed_at": "2026-10-10T03:31:55Z",
      "accepted_at": "2026-10-11T09:12:20Z",
      "published_at": "2026-10-11T09:12:22Z",
      "payload": {
        "listing": "lst_lum_p5_1t_hs",
        "from": "itm_ssd_p5_1t",
        "item": "itm_ssd_p5_1t_hs"
      },
      "history_epoch": "history-demo-1"
    }
  ]
}
