{
  "schemaVersion": 1,
  "slug": "webhook-reconciliation-deduplication",
  "locale": "en",
  "evidenceClass": "executed-local-fixture-not-provider-generation",
  "providerModelTest": false,
  "productionTaskRun": false,
  "runCommand": "node run-recipe.mjs --out ./my-run",
  "environment": {
    "nodeVersion": "v24.18.0",
    "ffmpeg": null
  },
  "title": "Reconcile duplicate, late and missing callbacks with a local ledger",
  "description": "Run an offline Node.js callback replay with duplicate-event rejection, terminal-state protection and one polling reconciliation for a missing callback.",
  "goal": "Keep one task identity while reconciling repeated or out-of-order delivery. This is an offline integration exercise; the site does not currently promise customer Webhooks.",
  "preparation": [
    "Download the complete kit and use Node.js. The recorded run used Node v24.18.0 with only its built-in HTTP, filesystem and crypto modules.",
    "Read fixture.json: local-A receives a success event twice and then a late processing event; local-B receives no callback and is recovered by one local status request.",
    "Choose a fresh output directory. The server binds an ephemeral port on 127.0.0.1, accepts only the two fixture task IDs and closes in finally. No provider credentials are used."
  ],
  "steps": [
    {
      "title": "01. Bind callbacks to a known task",
      "body": "Run the command from the unzipped kit. Before accepting an event, the receiver checks the task ID, event ID and its deliberately small processing/succeeded state vocabulary. The fixture rejects unknown tasks and an invented state with HTTP 400. These validation rules are local examples, not a provider payload specification."
    },
    {
      "title": "02. Apply each event once",
      "body": "Read ledger.json after the replay. evt-1 changes local-A to succeeded; replaying the same event ID is recorded as duplicate. The event set is a fixture store in memory. A deployed receiver would need durable event uniqueness and an atomic state update appropriate to its own database."
    },
    {
      "title": "03. Protect the completed state",
      "body": "The distinct evt-2 arrives late with processing. Its lower state rank is logged as stale, leaving local-A succeeded. This two-state policy demonstrates a terminal guard; it does not define cancellation, failure, retry or conflicting-terminal rules for a real API."
    },
    {
      "title": "04. Reconcile the absent callback",
      "body": "local-B remains processing until GET /status/local-B returns its matching task ID and succeeded state. Update the same ledger entry and verify one poll, zero /generate requests and a closed local server. The negative receipt also checks a poll result bound to the wrong task."
    }
  ],
  "acceptance": [
    "All ten named checks pass and the two executing script hashes match before and after the run.",
    "The initial terminal event is applied and its exact event ID replay is marked duplicate.",
    "The late processing event is marked stale and local-A remains succeeded.",
    "An unknown task and an invented state both return HTTP 400.",
    "local-B is pending before reconciliation and succeeded after one task-bound local poll.",
    "ledger.json reports zero generation requests and the loopback server is closed."
  ],
  "troubleshooting": [
    {
      "symptom": "The same delivery changes the task twice.",
      "change": "Use the event ID as the uniqueness key and rerun the exact evt-1 replay; the expected second action is duplicate."
    },
    {
      "symptom": "A completed task goes back to processing.",
      "change": "Change only the transition guard and replay evt-2 after success. The state must remain succeeded."
    },
    {
      "symptom": "A task never receives a callback.",
      "change": "Enable one task-bound reconciliation read for that pending identity. Check its returned task ID before applying state; do not submit another generation."
    }
  ],
  "ending": "Keep the replay ledger and receipts as evidence of this local reducer. Adapting it to a real service requires that service’s documented callback support, verification rules and durable storage; this kit makes no customer Webhook commitment.",
  "assetCaption": "Original event-lane diagram, locally replayed JSON events and an actual ten-check receipt. No provider task or model output was created."
}
