Содержание

NEG_REQUEST в PENDING: решение по Relationship и граница API scope

Назначение: Сценарий процесса. Продукт: ShariX Open.

Для кого: Разработчик · Поддержка · Рецензент и приёмщик · Нейросеть.

Темы: Обработчики → NEG_REQUEST · Доступ → Объектные права.

Навигация: База знаний → Сценарии → NEG_REQUEST / PENDING.

Область: ShariX Open. Обработчик: open_neg_request_pending, тип NEG_REQUEST (4), рабочий статус PENDING (420). Опубликованные источники повторно обновлены из Git 31.10.2026. Python заново получен из Blockly workspace; обе поставляемые Python-копии побайтово совпали, проверены 11 исходников генераторов из контракта. Сам обработчик при анализе не исполнялся. Фактическая server identity и выбранная локальная версия отдельно не проверялись.

Назначение и границы

Обработчик сопровождает запрос на Relationship до принятия либо отказа. Он не создаёт Relationship и не активирует её. Принятие переводит Ticket в ACCEPTED; требования и активация принадлежат следующему handler и backend gate. Это сценарий публичного dbsynce-домена ShariX Open. Он не описывает отдельные модели взаимоотношений ShariX Platform и не отождествляет клиента сервиса с клиентом платформы.

После проверки рабочего статуса выбирается ветвь:

Scoped список фильтруется по типу/статусу. Поэтому назначенный Ticket, который сохранил PENDING, может позже снова поступить через reconciliation и снова быть исключён.

Для неназначенного Ticket:

Известное расхождение опубликованных контрактов

Для open_neg_request_pending в catalog.py объявлены:

Но подготовка ручного уведомления выполняется в таком порядке:

  1. build_neg_request_notification_message вызывает _get_related_relationship.
  2. Requester читает Relationship через dbsynce/api/handler/ticket/{ticket_id}/relationship/.
  3. Backend проверяет capability related.relationship.read до возврата объекта.
  4. В указанном каталоге такого capability у PENDING нет.

Чтение происходит до отправки уведомления, а не после него.

При штатной проекции исследованного каталога в HANDLER_API_KEY_SCOPES такой read заканчивается 403. Исключение доходит до общего retry/defer processing_error; сообщение, manual_relationship_decision и штатный defer ручной ветви до завершения подготовки не достигаются. Отсутствие сообщения в этой ситуации не доказывает, что заявок нет или что её уже рассмотрели.

Это вывод из сопоставления свежих catalog, runtime, requester, handler auth и endpoint, а не результат вызова от имени реального ключа на конкретной установке. Сервер с иным явно настроенным scope может вести себя иначе; нужно сверить фактическую identity. Не следует обходить ошибку generic CRUD-ключом или ослаблением проверки API. Возможные направления исправления:

В обоих случаях нужны согласованные workspace/generation и проверки. В этой редакции документации программное исправление не выполнялось.

На BPMN оставлена как последовательность Python при успешном разрешённом read, так и явная boundary-ошибка 403. Эта страница не объявляет ручной путь безусловно работоспособным в стандартном каталоге.

Один проход и ожидание (BPMN)

Схема BPMN

Загрузка схемы…

Исходник схемы
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:ns2="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:ns3="http://www.omg.org/spec/DD/20100524/DC" xmlns:ns4="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_neg_pending_logic" targetNamespace="urn:sharix:knowledge:handlers">
  <bpmn:process id="neg_Process_neg" name="open_neg_request_pending: принятие запроса на Relationship и граница API capability" isExecutable="false">
    <bpmn:documentation>{"notes": ["Git built-in и внешние контракты, без выполнения обработчика.", "BPMN isExecutable=false. End event — конец показанного прохода, не предоставление права.", "deferred_until — символ локального срока, не настройка сервера.", "Известное расхождение базового каталога: ручное уведомление требует related.relationship.read, отсутствующего у NEG_PENDING. 403 возникает до notify/defer. Фактическая server identity проверяется отдельно."], "sources": [{"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "handlers/open_neg_request_pending.py", "repository": "sharix-open-handlers-examples", "sha256": "c1ce289487c6dcf04cdf76740721e12744dd62423e29605c556e0daac1e140b2"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "generated_handler_blocks/open_neg_request_pending.json", "repository": "sharix-open-handlers-examples", "sha256": "b9d64844c92e7002c77a1b2ff225bc4d72ebe3b889b85a32d664a6b7d748951e"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "catalog.py", "repository": "sharix-open-handlers-examples", "sha256": "e3dcb72b7e620e2837adacd7af053f35b04cdff475789496d9c114e17d57dfde"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/blockly_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "3e222150cbff315077c6c9b160d46e59856cedf6d6591d213e958c63cb2f2428"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/handler_operations.py", "repository": "sharix-open-handlers-backend", "sha256": "28620658501c375f556e24437871620b84a17a88d0b62b2d1a3cc5deb2f14b1e"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/ejabberd_muc.py", "repository": "sharix-open-handlers-backend", "sha256": "35c2eba89fab36b5c444439a87beaa516b55133991136d5370418d92a18d7bcd"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/requesters.py", "repository": "sharix-open-handlers-backend", "sha256": "965a348cea477722bbaf4c8c6b06023eb238b71664a332f7ea8c90bfe72ea808"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "bce71eab622922f8dd0954222842349799b60e18a0ab6310cc3e5f305603661e"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_codegen_contract.json", "repository": "sharix-open-handlers-backend", "sha256": "0bfd2b0ebb07bd380ed3c867386536ab88ef7dfa6e13f1cd8c91d32bb02c1ae9"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "lib/tickets.py", "repository": "sharix-open-tickets", "sha256": "0d260c17947f9a186d07af2749159780e3c7354a95b4bafe9d67493663c96883"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/views/handler_workflow.py", "repository": "sharix-open-tickets", "sha256": "f97c4b467a9a30749f5c4b86b39a833c430e57aaa1f030f70030d9b5c609d368"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/handler_auth.py", "repository": "sharix-open-tickets", "sha256": "ac76dc45aaa9742e5e1da0b98aae914bb2e287048d33e97b4a245a6997bc8425"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "utils.py", "repository": "sharix-open-tickets", "sha256": "a5680205c6522e1d9a74d9d267fe9ee96faf6f06436b00877fc0a93d80234bf5"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "views/ticket_detail.py", "repository": "sharix-open-tickets", "sha256": "05d344b0fba14cf895400694d4a18f486c800fef08dd1acb43ea46909b9e7e59"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "signals.py", "repository": "sharix-open-tickets", "sha256": "39aad8690a2e7d35cebfb052127a8130a951cbb4eea316c4f767a0a59fc5fa7e"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "services/handler_wakeup.py", "repository": "sharix-open-tickets", "sha256": "c9fdc62a18301fd7c075395c697d5b1c738c77713aa71fcbb5ab097e173d98de"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/handler_workflow.py", "repository": "sharix-open-backend", "sha256": "0f699a4e16760cbbdd09d894093c685ba69073fedf0522852cb1fdc9811636e3"}]}</bpmn:documentation>
    <bpmn:laneSet id="neg_Lanes_neg">
      <bpmn:lane id="neg_Lane_worker" name="Worker: один проход Python">
        <bpmn:flowNodeRef>neg_start</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_status</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_assigned</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_auto</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_notify</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_defer</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_cleanup</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_end</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_transition</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_confirmed</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_handoff</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_remove</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_accepted</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_missing</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_notify_error</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_api_error</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_send_error</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_retry</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_relationship</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_read_error</bpmn:flowNodeRef>
      </bpmn:lane>
      <bpmn:lane id="neg_Lane_queue" name="Планировщик: ожидание без занятого worker">
        <bpmn:flowNodeRef>neg_wait</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_timer</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_revision</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>neg_ready</bpmn:flowNodeRef>
      </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:startEvent id="neg_start" name="Ticket готов к проходу">
      <bpmn:outgoing>neg_Flow_0</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:exclusiveGateway id="neg_status" name="Статус PENDING?">
      <bpmn:incoming>neg_Flow_0</bpmn:incoming>
      <bpmn:incoming>neg_Flow_23</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_1</bpmn:outgoing>
      <bpmn:outgoing>neg_Flow_2</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:exclusiveGateway id="neg_assigned" name="assigned_to не None?">
      <bpmn:incoming>neg_Flow_1</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_3</bpmn:outgoing>
      <bpmn:outgoing>neg_Flow_4</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:exclusiveGateway id="neg_auto" name="NEGREQ_AUTOACCEPT?">
      <bpmn:incoming>neg_Flow_3</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_6</bpmn:outgoing>
      <bpmn:outgoing>neg_Flow_9</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:sendTask id="neg_notify" name="Сообщить в MUC-комнату заявки">
      <bpmn:documentation>METASERVICE-ADMIN, JID текущего bot и report JID; не автоматическая выдача роли.</bpmn:documentation>
      <bpmn:incoming>neg_Flow_24</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_7</bpmn:outgoing>
    </bpmn:sendTask>
    <bpmn:serviceTask id="neg_defer" name="manual_relationship_decision; defer">
      <bpmn:documentation>NEG_REQUEST_PENDING_RECHECK_SECONDS</bpmn:documentation>
      <bpmn:incoming>neg_Flow_7</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_8</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:serviceTask id="neg_cleanup" name="Убрать локальный Ticket и комнату">
      <bpmn:incoming>neg_Flow_2</bpmn:incoming>
      <bpmn:incoming>neg_Flow_4</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_5</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:endEvent id="neg_end" name="Текущий проход прекращён">
      <bpmn:incoming>neg_Flow_5</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:serviceTask id="neg_transition" name="Scoped API: ACCEPTED">
      <bpmn:incoming>neg_Flow_9</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_10</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:exclusiveGateway id="neg_confirmed" name="Переход подтверждён?">
      <bpmn:incoming>neg_Flow_10</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_11</bpmn:outgoing>
      <bpmn:outgoing>neg_Flow_12</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:sendTask id="neg_handoff" name="Передать в open_neg_request_accepted">
      <bpmn:incoming>neg_Flow_12</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_13</bpmn:outgoing>
    </bpmn:sendTask>
    <bpmn:serviceTask id="neg_remove" name="Убрать Ticket; комнату сохранить">
      <bpmn:incoming>neg_Flow_13</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_14</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:endEvent id="neg_accepted" name="Передача завершена; Relationship ещё проверяется">
      <bpmn:incoming>neg_Flow_14</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:endEvent id="neg_missing" name="404: локальная очистка запрошена">
      <bpmn:incoming>neg_Flow_11</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:boundaryEvent id="neg_notify_error" name="Ошибка уведомления" attachedToRef="neg_notify" cancelActivity="true">
      <bpmn:documentation>При этом вызове нет durable ack. Ошибка отправки — общий retry.</bpmn:documentation>
      <bpmn:outgoing>neg_Flow_15</bpmn:outgoing>
      <bpmn:errorEventDefinition />
    </bpmn:boundaryEvent>
    <bpmn:boundaryEvent id="neg_api_error" name="Ошибка API" attachedToRef="neg_transition" cancelActivity="true">
      <bpmn:outgoing>neg_Flow_16</bpmn:outgoing>
      <bpmn:errorEventDefinition />
    </bpmn:boundaryEvent>
    <bpmn:boundaryEvent id="neg_send_error" name="Ошибка отправки" attachedToRef="neg_handoff" cancelActivity="true">
      <bpmn:outgoing>neg_Flow_17</bpmn:outgoing>
      <bpmn:errorEventDefinition />
    </bpmn:boundaryEvent>
    <bpmn:serviceTask id="neg_retry" name="processing_error + общий defer">
      <bpmn:documentation>DEFERRED_TICKET_RECHECK_SECONDS; committed ACCEPTED не откатывается.</bpmn:documentation>
      <bpmn:incoming>neg_Flow_15</bpmn:incoming>
      <bpmn:incoming>neg_Flow_16</bpmn:incoming>
      <bpmn:incoming>neg_Flow_17</bpmn:incoming>
      <bpmn:incoming>neg_Flow_25</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_18</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:eventBasedGateway id="neg_wait" name="Что наступило раньше?">
      <bpmn:incoming>neg_Flow_8</bpmn:incoming>
      <bpmn:incoming>neg_Flow_18</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_19</bpmn:outgoing>
      <bpmn:outgoing>neg_Flow_20</bpmn:outgoing>
    </bpmn:eventBasedGateway>
    <bpmn:intermediateCatchEvent id="neg_timer" name="Локальный срок defer">
      <bpmn:incoming>neg_Flow_19</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_21</bpmn:outgoing>
      <bpmn:timerEventDefinition>
        <bpmn:timeDate xsi:type="bpmn:tFormalExpression">deferred_until</bpmn:timeDate>
      </bpmn:timerEventDefinition>
    </bpmn:intermediateCatchEvent>
    <bpmn:intermediateCatchEvent id="neg_revision" name="Новая authoritative revision">
      <bpmn:incoming>neg_Flow_20</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_22</bpmn:outgoing>
      <bpmn:messageEventDefinition />
    </bpmn:intermediateCatchEvent>
    <bpmn:serviceTask id="neg_ready" name="Снять defer, вернуть в ready FIFO">
      <bpmn:incoming>neg_Flow_21</bpmn:incoming>
      <bpmn:incoming>neg_Flow_22</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_23</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:serviceTask id="neg_relationship" name="Прочитать Relationship для текста уведомления">
      <bpmn:documentation>Требует related.relationship.read. В текущем базовом каталоге NEG_PENDING capability отсутствует.</bpmn:documentation>
      <bpmn:incoming>neg_Flow_6</bpmn:incoming>
      <bpmn:outgoing>neg_Flow_24</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:boundaryEvent id="neg_read_error" name="403: нет read capability" attachedToRef="neg_relationship" cancelActivity="true">
      <bpmn:outgoing>neg_Flow_25</bpmn:outgoing>
      <bpmn:errorEventDefinition />
    </bpmn:boundaryEvent>
    <bpmn:sequenceFlow id="neg_Flow_0" sourceRef="neg_start" targetRef="neg_status" name="" />
    <bpmn:sequenceFlow id="neg_Flow_1" sourceRef="neg_status" targetRef="neg_assigned" name="Да" />
    <bpmn:sequenceFlow id="neg_Flow_2" sourceRef="neg_status" targetRef="neg_cleanup" name="Нет" />
    <bpmn:sequenceFlow id="neg_Flow_3" sourceRef="neg_assigned" targetRef="neg_auto" name="Нет" />
    <bpmn:sequenceFlow id="neg_Flow_4" sourceRef="neg_assigned" targetRef="neg_cleanup" name="Да" />
    <bpmn:sequenceFlow id="neg_Flow_5" sourceRef="neg_cleanup" targetRef="neg_end" name="" />
    <bpmn:sequenceFlow id="neg_Flow_6" sourceRef="neg_auto" targetRef="neg_relationship" name="Нет" />
    <bpmn:sequenceFlow id="neg_Flow_7" sourceRef="neg_notify" targetRef="neg_defer" name="" />
    <bpmn:sequenceFlow id="neg_Flow_8" sourceRef="neg_defer" targetRef="neg_wait" name="" />
    <bpmn:sequenceFlow id="neg_Flow_9" sourceRef="neg_auto" targetRef="neg_transition" name="Да" />
    <bpmn:sequenceFlow id="neg_Flow_10" sourceRef="neg_transition" targetRef="neg_confirmed" name="" />
    <bpmn:sequenceFlow id="neg_Flow_11" sourceRef="neg_confirmed" targetRef="neg_missing" name="Нет (404)" />
    <bpmn:sequenceFlow id="neg_Flow_12" sourceRef="neg_confirmed" targetRef="neg_handoff" name="Да" />
    <bpmn:sequenceFlow id="neg_Flow_13" sourceRef="neg_handoff" targetRef="neg_remove" name="" />
    <bpmn:sequenceFlow id="neg_Flow_14" sourceRef="neg_remove" targetRef="neg_accepted" name="" />
    <bpmn:sequenceFlow id="neg_Flow_15" sourceRef="neg_notify_error" targetRef="neg_retry" name="" />
    <bpmn:sequenceFlow id="neg_Flow_16" sourceRef="neg_api_error" targetRef="neg_retry" name="" />
    <bpmn:sequenceFlow id="neg_Flow_17" sourceRef="neg_send_error" targetRef="neg_retry" name="" />
    <bpmn:sequenceFlow id="neg_Flow_18" sourceRef="neg_retry" targetRef="neg_wait" name="" />
    <bpmn:sequenceFlow id="neg_Flow_19" sourceRef="neg_wait" targetRef="neg_timer" name="" />
    <bpmn:sequenceFlow id="neg_Flow_20" sourceRef="neg_wait" targetRef="neg_revision" name="" />
    <bpmn:sequenceFlow id="neg_Flow_21" sourceRef="neg_timer" targetRef="neg_ready" name="" />
    <bpmn:sequenceFlow id="neg_Flow_22" sourceRef="neg_revision" targetRef="neg_ready" name="" />
    <bpmn:sequenceFlow id="neg_Flow_23" sourceRef="neg_ready" targetRef="neg_status" name="Следующий проход" />
    <bpmn:sequenceFlow id="neg_Flow_24" sourceRef="neg_relationship" targetRef="neg_notify" name="Если API разрешил" />
    <bpmn:sequenceFlow id="neg_Flow_25" sourceRef="neg_read_error" targetRef="neg_retry" name="" />
  </bpmn:process>
  <bpmn:collaboration id="Collaboration_neg_pending_logic">
    <bpmn:participant id="neg_Pool_neg" name="open_neg_request_pending: принятие запроса на Relationship и граница API capability" processRef="neg_Process_neg" />
  </bpmn:collaboration>
  <ns2:BPMNDiagram id="Diagram_neg_pending_logic">
    <ns2:BPMNPlane id="Plane_neg_pending_logic" bpmnElement="Collaboration_neg_pending_logic">
      <ns2:BPMNShape id="neg_DI_Pool_neg" bpmnElement="neg_Pool_neg" isHorizontal="true">
        <ns3:Bounds x="0.0" y="0.0" width="2080.0" height="970.0" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_Lane_worker" bpmnElement="neg_Lane_worker" isHorizontal="true">
        <ns3:Bounds x="30.0" y="0.0" width="2050.0" height="585.0" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_Lane_queue" bpmnElement="neg_Lane_queue" isHorizontal="true">
        <ns3:Bounds x="30.0" y="585.0" width="2050.0" height="385.0" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_start" bpmnElement="neg_start">
        <ns3:Bounds x="132.0" y="92.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_status" bpmnElement="neg_status">
        <ns3:Bounds x="325.0" y="85.0" width="50" height="50" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_assigned" bpmnElement="neg_assigned">
        <ns3:Bounds x="555.0" y="85.0" width="50" height="50" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_auto" bpmnElement="neg_auto">
        <ns3:Bounds x="805.0" y="85.0" width="50" height="50" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_notify" bpmnElement="neg_notify">
        <ns3:Bounds x="1265.0" y="75.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_defer" bpmnElement="neg_defer">
        <ns3:Bounds x="1525.0" y="75.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_cleanup" bpmnElement="neg_cleanup">
        <ns3:Bounds x="275.0" y="265.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_end" bpmnElement="neg_end">
        <ns3:Bounds x="562.0" y="282.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_transition" bpmnElement="neg_transition">
        <ns3:Bounds x="755.0" y="265.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_confirmed" bpmnElement="neg_confirmed">
        <ns3:Bounds x="1055.0" y="275.0" width="50" height="50" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_handoff" bpmnElement="neg_handoff">
        <ns3:Bounds x="1265.0" y="265.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_remove" bpmnElement="neg_remove">
        <ns3:Bounds x="1525.0" y="265.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_accepted" bpmnElement="neg_accepted">
        <ns3:Bounds x="1832.0" y="282.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_missing" bpmnElement="neg_missing">
        <ns3:Bounds x="1062.0" y="462.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_notify_error" bpmnElement="neg_notify_error">
        <ns3:Bounds x="1372.0" y="127.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_api_error" bpmnElement="neg_api_error">
        <ns3:Bounds x="862.0" y="317.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_send_error" bpmnElement="neg_send_error">
        <ns3:Bounds x="1372.0" y="317.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_retry" bpmnElement="neg_retry">
        <ns3:Bounds x="1525.0" y="465.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_wait" bpmnElement="neg_wait">
        <ns3:Bounds x="1055.0" y="715.0" width="50" height="50" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_timer" bpmnElement="neg_timer">
        <ns3:Bounds x="1322.0" y="652.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_revision" bpmnElement="neg_revision">
        <ns3:Bounds x="1322.0" y="852.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_ready" bpmnElement="neg_ready">
        <ns3:Bounds x="1525.0" y="705.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_relationship" bpmnElement="neg_relationship">
        <ns3:Bounds x="1005.0" y="75.0" width="150" height="70" />
      </ns2:BPMNShape>
      <ns2:BPMNShape id="neg_DI_read_error" bpmnElement="neg_read_error">
        <ns3:Bounds x="1112.0" y="127.0" width="36" height="36" />
      </ns2:BPMNShape>
      <ns2:BPMNEdge id="neg_DI_Flow_0" bpmnElement="neg_Flow_0">
        <ns4:waypoint x="168.0" y="110.0" />
        <ns4:waypoint x="325.0" y="110.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_1" bpmnElement="neg_Flow_1">
        <ns4:waypoint x="375.0" y="110.0" />
        <ns4:waypoint x="555.0" y="110.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_2" bpmnElement="neg_Flow_2">
        <ns4:waypoint x="350.0" y="135.0" />
        <ns4:waypoint x="350.0" y="265.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_3" bpmnElement="neg_Flow_3">
        <ns4:waypoint x="605.0" y="110.0" />
        <ns4:waypoint x="805.0" y="110.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_4" bpmnElement="neg_Flow_4">
        <ns4:waypoint x="605.0" y="110.0" />
        <ns4:waypoint x="690.0" y="110.0" />
        <ns4:waypoint x="690.0" y="235.0" />
        <ns4:waypoint x="350.0" y="235.0" />
        <ns4:waypoint x="350.0" y="265.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_5" bpmnElement="neg_Flow_5">
        <ns4:waypoint x="425.0" y="300.0" />
        <ns4:waypoint x="562.0" y="300.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_6" bpmnElement="neg_Flow_6">
        <ns4:waypoint x="855.0" y="110.0" />
        <ns4:waypoint x="1005.0" y="110.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_7" bpmnElement="neg_Flow_7">
        <ns4:waypoint x="1415.0" y="110.0" />
        <ns4:waypoint x="1525.0" y="110.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_8" bpmnElement="neg_Flow_8">
        <ns4:waypoint x="1675.0" y="110.0" />
        <ns4:waypoint x="1730.0" y="110.0" />
        <ns4:waypoint x="1730.0" y="600.0" />
        <ns4:waypoint x="1080.0" y="600.0" />
        <ns4:waypoint x="1080.0" y="715.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_9" bpmnElement="neg_Flow_9">
        <ns4:waypoint x="830.0" y="135.0" />
        <ns4:waypoint x="830.0" y="265.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_10" bpmnElement="neg_Flow_10">
        <ns4:waypoint x="905.0" y="300.0" />
        <ns4:waypoint x="1055.0" y="300.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_11" bpmnElement="neg_Flow_11">
        <ns4:waypoint x="1080.0" y="325.0" />
        <ns4:waypoint x="1080.0" y="462.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_12" bpmnElement="neg_Flow_12">
        <ns4:waypoint x="1105.0" y="300.0" />
        <ns4:waypoint x="1265.0" y="300.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_13" bpmnElement="neg_Flow_13">
        <ns4:waypoint x="1415.0" y="300.0" />
        <ns4:waypoint x="1525.0" y="300.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_14" bpmnElement="neg_Flow_14">
        <ns4:waypoint x="1675.0" y="300.0" />
        <ns4:waypoint x="1832.0" y="300.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_15" bpmnElement="neg_Flow_15">
        <ns4:waypoint x="1408.0" y="145.0" />
        <ns4:waypoint x="1190.0" y="145.0" />
        <ns4:waypoint x="1520.0" y="145.0" />
        <ns4:waypoint x="1520.0" y="435.0" />
        <ns4:waypoint x="1600.0" y="435.0" />
        <ns4:waypoint x="1600.0" y="465.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_16" bpmnElement="neg_Flow_16">
        <ns4:waypoint x="898.0" y="335.0" />
        <ns4:waypoint x="940.0" y="335.0" />
        <ns4:waypoint x="940.0" y="415.0" />
        <ns4:waypoint x="1600.0" y="415.0" />
        <ns4:waypoint x="1600.0" y="465.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_17" bpmnElement="neg_Flow_17">
        <ns4:waypoint x="1408.0" y="335.0" />
        <ns4:waypoint x="1450.0" y="335.0" />
        <ns4:waypoint x="1600.0" y="335.0" />
        <ns4:waypoint x="1600.0" y="465.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_18" bpmnElement="neg_Flow_18">
        <ns4:waypoint x="1675.0" y="500.0" />
        <ns4:waypoint x="1720.0" y="500.0" />
        <ns4:waypoint x="1720.0" y="615.0" />
        <ns4:waypoint x="1080.0" y="615.0" />
        <ns4:waypoint x="1080.0" y="715.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_19" bpmnElement="neg_Flow_19">
        <ns4:waypoint x="1105.0" y="740.0" />
        <ns4:waypoint x="1213.5" y="740.0" />
        <ns4:waypoint x="1213.5" y="670.0" />
        <ns4:waypoint x="1322.0" y="670.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_20" bpmnElement="neg_Flow_20">
        <ns4:waypoint x="1105.0" y="740.0" />
        <ns4:waypoint x="1213.5" y="740.0" />
        <ns4:waypoint x="1213.5" y="870.0" />
        <ns4:waypoint x="1322.0" y="870.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_21" bpmnElement="neg_Flow_21">
        <ns4:waypoint x="1358.0" y="670.0" />
        <ns4:waypoint x="1441.5" y="670.0" />
        <ns4:waypoint x="1441.5" y="740.0" />
        <ns4:waypoint x="1525.0" y="740.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_22" bpmnElement="neg_Flow_22">
        <ns4:waypoint x="1358.0" y="870.0" />
        <ns4:waypoint x="1441.5" y="870.0" />
        <ns4:waypoint x="1441.5" y="740.0" />
        <ns4:waypoint x="1525.0" y="740.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_23" bpmnElement="neg_Flow_23">
        <ns4:waypoint x="1675.0" y="740.0" />
        <ns4:waypoint x="2040.0" y="740.0" />
        <ns4:waypoint x="2040.0" y="25.0" />
        <ns4:waypoint x="350.0" y="25.0" />
        <ns4:waypoint x="350.0" y="85.0" />
        <ns2:BPMNLabel>
          <ns3:Bounds x="1750.0" y="5.0" width="180" height="26" />
        </ns2:BPMNLabel>
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_24" bpmnElement="neg_Flow_24">
        <ns4:waypoint x="1155.0" y="110.0" />
        <ns4:waypoint x="1265.0" y="110.0" />
      </ns2:BPMNEdge>
      <ns2:BPMNEdge id="neg_DI_Flow_25" bpmnElement="neg_Flow_25">
        <ns4:waypoint x="1148.0" y="145.0" />
        <ns4:waypoint x="1190.0" y="145.0" />
        <ns4:waypoint x="1520.0" y="145.0" />
        <ns4:waypoint x="1520.0" y="435.0" />
        <ns4:waypoint x="1600.0" y="435.0" />
        <ns4:waypoint x="1600.0" y="465.0" />
      </ns2:BPMNEdge>
    </ns2:BPMNPlane>
  </ns2:BPMNDiagram>
</bpmn:definitions>

При отключённом autoaccept успешный read включает в уведомление:

Это информационные поля связи; они не являются универсальной схемой назначения ролей и сами по себе не дают права принять Ticket. Если разрешённый read вернул 404, helper может сформировать текст «Relationship не найдена»; 403 является ошибкой и таким текстом не подменяется.

После успешного уведомления:

  1. Записать локальную причину manual_relationship_decision.
  2. Назначить повтор по NEG_REQUEST_PENDING_RECHECK_SECONDS.
  3. Освободить worker.

В этом пути нет durable verification ack: при повторных проходах сообщение может отправляться снова.

Автоматическая ветвь не готовит это уведомление и не выполняет данный read. Она запрашивает ACCEPTED через scoped Ticket API, передаёт Ticket в open_neg_request_accepted и убирает прежний локальный снимок с сохранением комнаты. Это не обход доменных требований активации в следующем handler. 404 при смене статуса запрашивает локальную очистку; иные ошибки API/отправки — общий retry без отката уже committed ACCEPTED.

Внешний пользователь и решение (BPMN)

Схема BPMN

Загрузка схемы…

Исходник схемы
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_neg_pending_external" targetNamespace="urn:sharix:knowledge:handlers">
  <bpmn:process id="person_Process_person" name="Ответственный пользователь с отдельным доступом к Ticket" isExecutable="false">
    <bpmn:documentation>{"notes": ["Git built-in и внешние контракты, без выполнения обработчика.", "BPMN isExecutable=false. End event — конец показанного прохода, не предоставление права.", "deferred_until — символ локального срока, не настройка сервера.", "Участие в XMPP комнате не выдаёт право менять Ticket. Текст сообщения не является командой смены статуса.", "Известное расхождение базового каталога: ручное уведомление требует related.relationship.read, отсутствующего у NEG_PENDING. 403 возникает до notify/defer. Фактическая server identity проверяется отдельно."], "sources": [{"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "handlers/open_neg_request_pending.py", "repository": "sharix-open-handlers-examples", "sha256": "c1ce289487c6dcf04cdf76740721e12744dd62423e29605c556e0daac1e140b2"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "generated_handler_blocks/open_neg_request_pending.json", "repository": "sharix-open-handlers-examples", "sha256": "b9d64844c92e7002c77a1b2ff225bc4d72ebe3b889b85a32d664a6b7d748951e"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "catalog.py", "repository": "sharix-open-handlers-examples", "sha256": "e3dcb72b7e620e2837adacd7af053f35b04cdff475789496d9c114e17d57dfde"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/blockly_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "3e222150cbff315077c6c9b160d46e59856cedf6d6591d213e958c63cb2f2428"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/handler_operations.py", "repository": "sharix-open-handlers-backend", "sha256": "28620658501c375f556e24437871620b84a17a88d0b62b2d1a3cc5deb2f14b1e"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/ejabberd_muc.py", "repository": "sharix-open-handlers-backend", "sha256": "35c2eba89fab36b5c444439a87beaa516b55133991136d5370418d92a18d7bcd"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/requesters.py", "repository": "sharix-open-handlers-backend", "sha256": "965a348cea477722bbaf4c8c6b06023eb238b71664a332f7ea8c90bfe72ea808"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "bce71eab622922f8dd0954222842349799b60e18a0ab6310cc3e5f305603661e"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_codegen_contract.json", "repository": "sharix-open-handlers-backend", "sha256": "0bfd2b0ebb07bd380ed3c867386536ab88ef7dfa6e13f1cd8c91d32bb02c1ae9"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "lib/tickets.py", "repository": "sharix-open-tickets", "sha256": "0d260c17947f9a186d07af2749159780e3c7354a95b4bafe9d67493663c96883"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/views/handler_workflow.py", "repository": "sharix-open-tickets", "sha256": "f97c4b467a9a30749f5c4b86b39a833c430e57aaa1f030f70030d9b5c609d368"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/handler_auth.py", "repository": "sharix-open-tickets", "sha256": "ac76dc45aaa9742e5e1da0b98aae914bb2e287048d33e97b4a245a6997bc8425"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "utils.py", "repository": "sharix-open-tickets", "sha256": "a5680205c6522e1d9a74d9d267fe9ee96faf6f06436b00877fc0a93d80234bf5"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "views/ticket_detail.py", "repository": "sharix-open-tickets", "sha256": "05d344b0fba14cf895400694d4a18f486c800fef08dd1acb43ea46909b9e7e59"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "signals.py", "repository": "sharix-open-tickets", "sha256": "39aad8690a2e7d35cebfb052127a8130a951cbb4eea316c4f767a0a59fc5fa7e"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "services/handler_wakeup.py", "repository": "sharix-open-tickets", "sha256": "c9fdc62a18301fd7c075395c697d5b1c738c77713aa71fcbb5ab097e173d98de"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/handler_workflow.py", "repository": "sharix-open-backend", "sha256": "0f699a4e16760cbbdd09d894093c685ba69073fedf0522852cb1fdc9811636e3"}]}</bpmn:documentation>
    <bpmn:laneSet id="person_Lanes_person">
      <bpmn:lane id="person_Lane_worker" name="Ручное решение">
        <bpmn:flowNodeRef>person_notice</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>person_review</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>person_choose</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>person_send</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>person_end</bpmn:flowNodeRef>
      </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:startEvent id="person_notice" name="Получено сообщение о заявке">
      <bpmn:outgoing>person_Flow_0</bpmn:outgoing>
      <bpmn:messageEventDefinition />
    </bpmn:startEvent>
    <bpmn:userTask id="person_review" name="Изучить заявку и основание доступа">
      <bpmn:incoming>person_Flow_0</bpmn:incoming>
      <bpmn:outgoing>person_Flow_1</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:userTask id="person_choose" name="Выбрать ACCEPTED / DECLINED">
      <bpmn:incoming>person_Flow_1</bpmn:incoming>
      <bpmn:outgoing>person_Flow_2</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:sendTask id="person_send" name="Запросить переход в карточке/API">
      <bpmn:incoming>person_Flow_2</bpmn:incoming>
      <bpmn:outgoing>person_Flow_3</bpmn:outgoing>
    </bpmn:sendTask>
    <bpmn:endEvent id="person_end" name="Ожидать результата сохранения">
      <bpmn:incoming>person_Flow_3</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="person_Flow_0" sourceRef="person_notice" targetRef="person_review" name="" />
    <bpmn:sequenceFlow id="person_Flow_1" sourceRef="person_review" targetRef="person_choose" name="" />
    <bpmn:sequenceFlow id="person_Flow_2" sourceRef="person_choose" targetRef="person_send" name="" />
    <bpmn:sequenceFlow id="person_Flow_3" sourceRef="person_send" targetRef="person_end" name="" />
  </bpmn:process>
  <bpmn:process id="backend_Process_backend" name="Tickets backend: объектные права, lifecycle и commit" isExecutable="false">
    <bpmn:documentation>{"notes": ["Git built-in и внешние контракты, без выполнения обработчика.", "BPMN isExecutable=false. End event — конец показанного прохода, не предоставление права.", "deferred_until — символ локального срока, не настройка сервера.", "Участие в XMPP комнате не выдаёт право менять Ticket. Текст сообщения не является командой смены статуса.", "Известное расхождение базового каталога: ручное уведомление требует related.relationship.read, отсутствующего у NEG_PENDING. 403 возникает до notify/defer. Фактическая server identity проверяется отдельно."], "sources": [{"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "handlers/open_neg_request_pending.py", "repository": "sharix-open-handlers-examples", "sha256": "c1ce289487c6dcf04cdf76740721e12744dd62423e29605c556e0daac1e140b2"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "generated_handler_blocks/open_neg_request_pending.json", "repository": "sharix-open-handlers-examples", "sha256": "b9d64844c92e7002c77a1b2ff225bc4d72ebe3b889b85a32d664a6b7d748951e"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "catalog.py", "repository": "sharix-open-handlers-examples", "sha256": "e3dcb72b7e620e2837adacd7af053f35b04cdff475789496d9c114e17d57dfde"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/blockly_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "3e222150cbff315077c6c9b160d46e59856cedf6d6591d213e958c63cb2f2428"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/handler_operations.py", "repository": "sharix-open-handlers-backend", "sha256": "28620658501c375f556e24437871620b84a17a88d0b62b2d1a3cc5deb2f14b1e"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/ejabberd_muc.py", "repository": "sharix-open-handlers-backend", "sha256": "35c2eba89fab36b5c444439a87beaa516b55133991136d5370418d92a18d7bcd"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/requesters.py", "repository": "sharix-open-handlers-backend", "sha256": "965a348cea477722bbaf4c8c6b06023eb238b71664a332f7ea8c90bfe72ea808"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "bce71eab622922f8dd0954222842349799b60e18a0ab6310cc3e5f305603661e"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_codegen_contract.json", "repository": "sharix-open-handlers-backend", "sha256": "0bfd2b0ebb07bd380ed3c867386536ab88ef7dfa6e13f1cd8c91d32bb02c1ae9"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "lib/tickets.py", "repository": "sharix-open-tickets", "sha256": "0d260c17947f9a186d07af2749159780e3c7354a95b4bafe9d67493663c96883"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/views/handler_workflow.py", "repository": "sharix-open-tickets", "sha256": "f97c4b467a9a30749f5c4b86b39a833c430e57aaa1f030f70030d9b5c609d368"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/handler_auth.py", "repository": "sharix-open-tickets", "sha256": "ac76dc45aaa9742e5e1da0b98aae914bb2e287048d33e97b4a245a6997bc8425"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "utils.py", "repository": "sharix-open-tickets", "sha256": "a5680205c6522e1d9a74d9d267fe9ee96faf6f06436b00877fc0a93d80234bf5"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "views/ticket_detail.py", "repository": "sharix-open-tickets", "sha256": "05d344b0fba14cf895400694d4a18f486c800fef08dd1acb43ea46909b9e7e59"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "signals.py", "repository": "sharix-open-tickets", "sha256": "39aad8690a2e7d35cebfb052127a8130a951cbb4eea316c4f767a0a59fc5fa7e"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "services/handler_wakeup.py", "repository": "sharix-open-tickets", "sha256": "c9fdc62a18301fd7c075395c697d5b1c738c77713aa71fcbb5ab097e173d98de"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/handler_workflow.py", "repository": "sharix-open-backend", "sha256": "0f699a4e16760cbbdd09d894093c685ba69073fedf0522852cb1fdc9811636e3"}]}</bpmn:documentation>
    <bpmn:laneSet id="backend_Lanes_backend">
      <bpmn:lane id="backend_Lane_worker" name="Сохранение решения">
        <bpmn:flowNodeRef>backend_request</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>backend_acl</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>backend_allowed</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>backend_save</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>backend_publish</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>backend_end</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>backend_reject</bpmn:flowNodeRef>
      </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:startEvent id="backend_request" name="Запрос решения">
      <bpmn:outgoing>backend_Flow_0</bpmn:outgoing>
      <bpmn:messageEventDefinition />
    </bpmn:startEvent>
    <bpmn:serviceTask id="backend_acl" name="Проверить доступ и допустимость перехода">
      <bpmn:incoming>backend_Flow_0</bpmn:incoming>
      <bpmn:outgoing>backend_Flow_1</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:exclusiveGateway id="backend_allowed" name="Разрешено?">
      <bpmn:incoming>backend_Flow_1</bpmn:incoming>
      <bpmn:outgoing>backend_Flow_2</bpmn:outgoing>
      <bpmn:outgoing>backend_Flow_3</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:serviceTask id="backend_save" name="Сохранить статус и Event">
      <bpmn:incoming>backend_Flow_2</bpmn:incoming>
      <bpmn:outgoing>backend_Flow_4</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:sendTask id="backend_publish" name="После commit: changed / moved">
      <bpmn:incoming>backend_Flow_4</bpmn:incoming>
      <bpmn:outgoing>backend_Flow_5</bpmn:outgoing>
    </bpmn:sendTask>
    <bpmn:endEvent id="backend_end" name="Решение сохранено">
      <bpmn:incoming>backend_Flow_5</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:endEvent id="backend_reject" name="Отказ: переход не выполнен">
      <bpmn:incoming>backend_Flow_3</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="backend_Flow_0" sourceRef="backend_request" targetRef="backend_acl" name="" />
    <bpmn:sequenceFlow id="backend_Flow_1" sourceRef="backend_acl" targetRef="backend_allowed" name="" />
    <bpmn:sequenceFlow id="backend_Flow_2" sourceRef="backend_allowed" targetRef="backend_save" name="Да" />
    <bpmn:sequenceFlow id="backend_Flow_3" sourceRef="backend_allowed" targetRef="backend_reject" name="Нет" />
    <bpmn:sequenceFlow id="backend_Flow_4" sourceRef="backend_save" targetRef="backend_publish" name="" />
    <bpmn:sequenceFlow id="backend_Flow_5" sourceRef="backend_publish" targetRef="backend_end" name="" />
  </bpmn:process>
  <bpmn:collaboration id="Collaboration_neg_pending_external">
    <bpmn:participant id="room_Participant" name="MUC-комната ticket_&lt;id&gt;_type_4: уведомление при успешном разрешённом read" />
    <bpmn:participant id="person_Pool_person" name="Ответственный пользователь с отдельным доступом к Ticket" processRef="person_Process_person" />
    <bpmn:participant id="backend_Pool_backend" name="Tickets backend: объектные права, lifecycle и commit" processRef="backend_Process_backend" />
    <bpmn:participant id="old_Participant" name="PENDING handler: исключить прежний снимок; fallback reconciliation" />
    <bpmn:participant id="next_Participant" name="ACCEPTED / DECLINED handler: профильные проверки и действия" />
    <bpmn:messageFlow id="Message_neg_pending_external_0" sourceRef="room_Participant" targetRef="person_notice" name="Уведомление, не разрешение доступа" />
    <bpmn:messageFlow id="Message_neg_pending_external_1" sourceRef="person_send" targetRef="backend_request" name="Ручное решение через разрешённый интерфейс" />
    <bpmn:messageFlow id="Message_neg_pending_external_2" sourceRef="backend_publish" targetRef="old_Participant" name="ticket.moved: прежний PENDING" />
    <bpmn:messageFlow id="Message_neg_pending_external_3" sourceRef="backend_publish" targetRef="next_Participant" name="ticket.changed: новый владелец" />
  </bpmn:collaboration>
  <bpmndi:BPMNDiagram id="Diagram_neg_pending_external">
    <bpmndi:BPMNPlane id="Plane_neg_pending_external" bpmnElement="Collaboration_neg_pending_external">
      <bpmndi:BPMNShape id="DI_room_Participant" bpmnElement="room_Participant" isHorizontal="true">
        <dc:Bounds x="0" y="0" width="1700" height="90" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="person_DI_Pool_person" bpmnElement="person_Pool_person" isHorizontal="true">
        <dc:Bounds x="0.0" y="210.0" width="1400" height="200" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="person_DI_Lane_worker" bpmnElement="person_Lane_worker" isHorizontal="true">
        <dc:Bounds x="30.0" y="210.0" width="1370" height="200" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="person_DI_notice" bpmnElement="person_notice">
        <dc:Bounds x="132.0" y="292.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="person_DI_review" bpmnElement="person_review">
        <dc:Bounds x="345.0" y="275.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="person_DI_choose" bpmnElement="person_choose">
        <dc:Bounds x="625.0" y="275.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="person_DI_send" bpmnElement="person_send">
        <dc:Bounds x="905.0" y="275.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="person_DI_end" bpmnElement="person_end">
        <dc:Bounds x="1222.0" y="292.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="person_DI_Flow_0" bpmnElement="person_Flow_0">
        <di:waypoint x="168.0" y="310.0" />
        <di:waypoint x="345.0" y="310.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="person_DI_Flow_1" bpmnElement="person_Flow_1">
        <di:waypoint x="495.0" y="310.0" />
        <di:waypoint x="625.0" y="310.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="person_DI_Flow_2" bpmnElement="person_Flow_2">
        <di:waypoint x="775.0" y="310.0" />
        <di:waypoint x="905.0" y="310.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="person_DI_Flow_3" bpmnElement="person_Flow_3">
        <di:waypoint x="1055.0" y="310.0" />
        <di:waypoint x="1222.0" y="310.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="backend_DI_Pool_backend" bpmnElement="backend_Pool_backend" isHorizontal="true">
        <dc:Bounds x="0.0" y="560.0" width="1630" height="380" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_Lane_worker" bpmnElement="backend_Lane_worker" isHorizontal="true">
        <dc:Bounds x="30.0" y="560.0" width="1600" height="380" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_request" bpmnElement="backend_request">
        <dc:Bounds x="132.0" y="642.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_acl" bpmnElement="backend_acl">
        <dc:Bounds x="345.0" y="625.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_allowed" bpmnElement="backend_allowed">
        <dc:Bounds x="675.0" y="635.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_save" bpmnElement="backend_save">
        <dc:Bounds x="875.0" y="625.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_publish" bpmnElement="backend_publish">
        <dc:Bounds x="1145.0" y="625.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_end" bpmnElement="backend_end">
        <dc:Bounds x="1452.0" y="642.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="backend_DI_reject" bpmnElement="backend_reject">
        <dc:Bounds x="682.0" y="822.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="backend_DI_Flow_0" bpmnElement="backend_Flow_0">
        <di:waypoint x="168.0" y="660.0" />
        <di:waypoint x="345.0" y="660.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="backend_DI_Flow_1" bpmnElement="backend_Flow_1">
        <di:waypoint x="495.0" y="660.0" />
        <di:waypoint x="675.0" y="660.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="backend_DI_Flow_2" bpmnElement="backend_Flow_2">
        <di:waypoint x="725.0" y="660.0" />
        <di:waypoint x="875.0" y="660.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="backend_DI_Flow_3" bpmnElement="backend_Flow_3">
        <di:waypoint x="700.0" y="685.0" />
        <di:waypoint x="700.0" y="822.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="backend_DI_Flow_4" bpmnElement="backend_Flow_4">
        <di:waypoint x="1025.0" y="660.0" />
        <di:waypoint x="1145.0" y="660.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="backend_DI_Flow_5" bpmnElement="backend_Flow_5">
        <di:waypoint x="1295.0" y="660.0" />
        <di:waypoint x="1452.0" y="660.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="DI_old_Participant" bpmnElement="old_Participant" isHorizontal="true">
        <dc:Bounds x="0" y="1090" width="760" height="100" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="DI_next_Participant" bpmnElement="next_Participant" isHorizontal="true">
        <dc:Bounds x="850" y="1090" width="850" height="100" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="DI_Message_neg_pending_external_0" bpmnElement="Message_neg_pending_external_0">
        <di:waypoint x="150" y="90" />
        <di:waypoint x="150" y="292" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="170" y="140" width="260" height="42" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="DI_Message_neg_pending_external_1" bpmnElement="Message_neg_pending_external_1">
        <di:waypoint x="980" y="345" />
        <di:waypoint x="980" y="510" />
        <di:waypoint x="150" y="510" />
        <di:waypoint x="150" y="642" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="260" y="480" width="260" height="42" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="DI_Message_neg_pending_external_2" bpmnElement="Message_neg_pending_external_2">
        <di:waypoint x="1220" y="695" />
        <di:waypoint x="1220" y="940" />
        <di:waypoint x="380" y="940" />
        <di:waypoint x="380" y="1090" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="210" y="985" width="260" height="42" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="DI_Message_neg_pending_external_3" bpmnElement="Message_neg_pending_external_3">
        <di:waypoint x="1280" y="695" />
        <di:waypoint x="1650" y="695" />
        <di:waypoint x="1650" y="1010" />
        <di:waypoint x="1275" y="1010" />
        <di:waypoint x="1275" y="1090" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="1080" y="1040" width="260" height="42" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

Схема ручного рассмотрения начинается только после успешной доставки/получения сообщения, а не гарантирует его при описанном расхождении scope. MUC-комната ticket_<id>_type_4 использует общий helper: активные METASERVICE-ADMIN с XMPP-реквизитами, текущий bot и report JID. Это не выбор получателей по одному company/neg_type и не выдача им доступа к Ticket. Подготовка комнаты имеет собственные логируемые ошибки; успешная локальная запись о комнате не доказывает доставку всем участникам.

Пользователь изучает основание запроса и выполняет действие через разрешённую карточку/API. Ответ текстом в комнате этот handler не трактует как команду смены статуса. Из PENDING текущий lifecycle допускает ACCEPTED, DECLINED либо CLOSED; схема показывает два профильных решения. Закрытие напрямую не доказывает активацию Relationship. Авторство не даёт права принять собственную заявку: view/API проверяют объектный доступ и допустимость перехода.

После commit ticket.changed будит нового владельца, ticket.moved исключает прежний локальный снимок, при занятом worker — через pending discard. Потеря XMPP-доставки не отменяет сохранённый статус и восстанавливается periodic reconciliation.

Таймеры, ошибки и диагностика

Ситуация Повтор / результат
Ручное уведомление успешно подготовлено и отправлено NEG_REQUEST_PENDING_RECHECK_SECONDS, manual_relationship_decision
Нет read capability при подготовке уведомления 403 до notify; общий processing_error
Иное исключение API/отправки DEFERRED_TICKET_RECHECK_SECONDS, processing_error
Потеря сообщения / восстановление состава INTERVAL_TIME_BD_REQ, scoped PENDING-список

При диагностике времени и очереди учитывать:

Порядок предметной диагностики:

Смена имени настройки или ручное выставление active не исправляют расхождение контракта уведомления.

Связанные материалы

Исходники, сверенные 31.10.2026

Проверены опубликованные исходники указанных commits. Это анализ кода, а не подтверждение состояния сервера или выполнения процесса в установленной системе.