{
  "schemaVersion": 1,
  "id": "P49-C02",
  "title": "Запись на один слот: гонка двух заявок и повтор ответа",
  "author": {
    "id": "P49",
    "name": "Григорий Никитин"
  },
  "specialty": "Проверка качества ИИ-помощников",
  "projectStatus": "concept",
  "source": {
    "document": "catalogue_60_personas.md",
    "briefId": "P49-C02",
    "title": "Бот записи",
    "kind": "editorial_concept",
    "data": "synthetic"
  },
  "context": "Слот 18:00 запрашивают Анна и Борис. Одновременно повторяется запрос Анны, а после сохранения имитируется потеря ответа. Без ключа запроса повтор выглядит новой записью.",
  "decision": "Проверка ключа и занятие слота находятся в одном синхронном участке после общей асинхронной границы. Я отдельно запускаю три Promise и сохраняю фактический результат. В интерфейсе можно разобрать ту же последовательность событий.",
  "constraint": "Структура хранится в памяти одного процесса. Свойства PostgreSQL, нескольких серверов и сохранность после перезапуска не заявляются.",
  "steps": [
    "Зафиксировать вход и ожидаемое поведение до запуска.",
    "Выполнить обработчик на обычном и конфликтном сценарии.",
    "Сохранить фактический журнал и повторяемую проверку."
  ],
  "inspection": [
    "Две личности и повтор не создают три брони — проверено.",
    "Другой слот допускает вторую запись — проверено.",
    "Повтор после потерянного ответа возвращает сохраненное решение — проверено."
  ],
  "sample": {
    "headers": [
      "Ключ",
      "Клиент",
      "Слот",
      "Статус",
      "Повтор"
    ],
    "rows": [
      [
        "A1",
        "anna",
        "18:00",
        "Подтверждено",
        "Нет"
      ],
      [
        "B1",
        "boris",
        "18:00",
        "Конфликт",
        "Нет"
      ],
      [
        "A1",
        "anna",
        "18:00",
        "Подтверждено",
        "Да"
      ]
    ]
  },
  "sampleNote": "Авторский набор событий и полные тексты. Все решения выполняются локальным кодом, внешняя модель не вызывается.",
  "caseStudy": {
    "role": "Инженер проверки сценариев и доступа",
    "contribution": "Я исполнил три конкурентных запроса к одному слоту и сохранил исходы записи, конфликта и повтора.",
    "objective": "Три одновременно начатых запроса дают одну бронь, один конфликт и тот же ответ на повтор.",
    "problem": "Слот 18:00 запрашивают Анна и Борис. Одновременно повторяется запрос Анны, а после сохранения имитируется потеря ответа. Без ключа запроса повтор выглядит новой записью.",
    "result": "Реально выполненный асинхронный прогон трех запросов сохранил одну бронь. Борис получил конфликт, повтор A1 — первоначальное подтверждение. Последующий повтор после потери ответа не создал новую запись.",
    "deliverables": [
      "index.html",
      "source.json",
      "source.csv",
      "result.json",
      "result.csv",
      "engine.js",
      "checks.json",
      "style.css",
      "concurrency-result.json"
    ],
    "blocks": [
      {
        "type": "text",
        "title": "Три запроса на слот 18",
        "text": "Одновременные обращения к слоту 18 запускаются через Promise, а не последовательно на бумаге. Один запрос фиксирует запись, другой получает конфликт, повтор возвращается к прежнему результату. Эта проверка полезна именно конкуренцией: при последовательном запуске слабое место между чтением и записью могло бы остаться незаметным."
      }
    ]
  },
  "deliverable": {
    "type": "app",
    "entry": "/catalogue/deliverables/p49-c02/index.html",
    "label": "Открыть работающий материал",
    "files": [
      {
        "path": "/catalogue/deliverables/p49-c02/index.html",
        "name": "index.html",
        "mimeType": "text/html"
      },
      {
        "path": "/catalogue/deliverables/p49-c02/source.json",
        "name": "source.json",
        "mimeType": "application/json"
      },
      {
        "path": "/catalogue/deliverables/p49-c02/source.csv",
        "name": "source.csv",
        "mimeType": "text/csv"
      },
      {
        "path": "/catalogue/deliverables/p49-c02/result.json",
        "name": "result.json",
        "mimeType": "application/json"
      },
      {
        "path": "/catalogue/deliverables/p49-c02/result.csv",
        "name": "result.csv",
        "mimeType": "text/csv"
      },
      {
        "path": "/catalogue/deliverables/p49-c02/engine.js",
        "name": "engine.js",
        "mimeType": "text/javascript"
      },
      {
        "path": "/catalogue/deliverables/p49-c02/verify.cjs",
        "name": "verify.cjs",
        "mimeType": "text/javascript"
      },
      {
        "path": "/catalogue/deliverables/p49-c02/checks.json",
        "name": "checks.json",
        "mimeType": "application/json"
      },
      {
        "path": "/catalogue/deliverables/p49-c02/style.css",
        "name": "style.css",
        "mimeType": "text/css"
      },
      {
        "path": "/catalogue/deliverables/p49-c02/concurrency-check.cjs",
        "name": "concurrency-check.cjs",
        "mimeType": "text/javascript"
      },
      {
        "path": "/catalogue/deliverables/p49-c02/concurrency-result.json",
        "name": "concurrency-result.json",
        "mimeType": "application/json"
      }
    ],
    "summary": "Реально выполненный асинхронный прогон трех запросов сохранил одну бронь. Борис получил конфликт, повтор A1 — первоначальное подтверждение. Последующий повтор после потери ответа не создал новую запись."
  },
  "takeaway": "Реально выполненный асинхронный прогон трех запросов сохранил одну бронь. Борис получил конфликт, повтор A1 — первоначальное подтверждение. Последующий повтор после потери ответа не создал новую запись.",
  "capabilities": [
    "completed_app"
  ],
  "externalConnections": []
}
