[
  {
    "id": "AW-01",
    "title": "Resolver decision (review or automatic rule)",
    "owner": "mod_review / mod_identity_relationships",
    "status": "PROP",
    "stages": [
      [
        "BEGIN",
        "resolver tx (READ COMMITTED + row locks; isolation choice validated in SPK-01)",
        "PROP"
      ],
      [
        "Coordinate stream writer",
        "Lock the cursor row for tenant_id + catalog_id + history_epoch BEFORE changing exported state. Update the transactional counter, accepted rows and outbox in one transaction. Rollback consumes no position. A delayed writer blocks later commits in this stream.",
        "PROP"
      ],
      [
        "Check case revision",
        "UPDATE review_case SET revision = revision + 1 WHERE id AND revision = expected (INV-04)",
        "EST"
      ],
      [
        "Insert decision",
        "Immutable row: action, actor, rationale, evidence refs, prior decision refs",
        "PROP"
      ],
      [
        "Apply facts",
        "Item / mapping / attribute rows with revision + 1; check INV-01/02/07",
        "PROP"
      ],
      [
        "Insert outbox rows",
        "Allocate consecutive committed positions with the transactional cursor; one event per affected aggregate revision. Include tenant, catalog, history_epoch, event id, revision, dependencies and payload in the same transaction as decision and facts.",
        "EST"
      ],
      [
        "COMMIT",
        "All or nothing",
        "EST"
      ],
      [
        "Relay (after commit)",
        "Publishes outbox rows to the product-ingestion queue; duplicates possible",
        "EST"
      ]
    ]
  },
  {
    "id": "AW-02",
    "title": "Capture intake",
    "owner": "mod_intake_evidence",
    "status": "PROP",
    "stages": [
      [
        "Authenticate + validate",
        "Capture schema version, idempotency key (credential model: see Trust & tenancy)",
        "PROP"
      ],
      [
        "Put artifact",
        "Content-addressed, put-if-absent in the evidence bucket (before the tx)",
        "PROP"
      ],
      [
        "BEGIN",
        "resolver tx",
        "PROP"
      ],
      [
        "Insert capture + receipt + job row",
        "Duplicate idempotency key → return the existing receipt",
        "PROP"
      ],
      [
        "COMMIT",
        "Receipt is now durable",
        "PROP"
      ],
      [
        "Enqueue job (ref)",
        "After commit; on failure the pending-job sweeper re-enqueues",
        "PROP"
      ]
    ]
  },
  {
    "id": "AW-03",
    "title": "Outbox relay",
    "owner": "svc_resolver (relay)",
    "status": "PROP",
    "stages": [
      [
        "Read batch",
        "SELECT unpublished rows ORDER BY seq LIMIT batch (FOR UPDATE SKIP LOCKED if several relays)",
        "PROP"
      ],
      [
        "Publish",
        "Send batch to the product-ingestion queue; message carries event id + small payload or ref",
        "PROP"
      ],
      [
        "Mark published",
        "UPDATE published_at; crash between publish and mark → re-publish (duplicate)",
        "PROP"
      ],
      [
        "Prune",
        "Prune only a contiguous prefix of published events whose retention age has elapsed, through the minimum of every registered live consumer’s durable contiguous progress and every unexpired rebuild lease pin. Serialize lease creation/advancement, consumer registration and pruning with the retention-control lock. Delivery or acknowledgement alone is not application progress; a missing position or parked dependency protects the tail. Monitor blocked retention and lease expiry; never silently remove a lagging live consumer.",
        "PROP"
      ]
    ]
  },
  {
    "id": "AW-04",
    "title": "Product consumer apply",
    "owner": "svc_product",
    "status": "EST",
    "stages": [
      [
        "Receive",
        "At-least-once delivery (Queues, verified 2026-10-12)",
        "EST"
      ],
      [
        "BEGIN",
        "product tx",
        "EST"
      ],
      [
        "Inbox insert",
        "INSERT inbox(event_id) ON CONFLICT DO NOTHING; conflict → duplicate → COMMIT + ack",
        "EST"
      ],
      [
        "Revision check",
        "Event rev ≤ applied rev for the aggregate → record as ignored",
        "EST"
      ],
      [
        "Dependency check",
        "Missing dependency revision → park the event (parked row) and ack; released when the dependency applies",
        "EST"
      ],
      [
        "Apply projection",
        "Upsert projection rows; set applied rev",
        "EST"
      ],
      [
        "Commit contiguous progress",
        "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.",
        "PROP"
      ],
      [
        "COMMIT, then ack",
        "Ack only after commit; crash before ack → redelivery → inbox dedupe",
        "EST"
      ]
    ]
  }
]