Сборщик служебных отчётов open_report_handler
Назначение и канонический контракт
Сборщик выполняет две независимые части:
Это не постоянный журнал ошибок, очередь Deck или редакторское согласование. Доставка каждому сотруднику не реализована.
В Blockly зафиксирована typed operation aggregate_handler_reports. Сгенерированный Python вызывает binding; runtime сверяет точную последовательность с каталогом builtin и загружает first_party_operations/open_report_handler.py. При несовпадении контракт отклоняется. В runtime исполняется Python; Blockly JSON не интерпретируется. Код operation и общий launcher включены в закреплённые источники, поскольку wrapper сам по себе не описывает фильтры, память и ошибки пересылки.
BPMN описательные, isExecutable=false. Два вида показывают независимый входящий callback и длительный цикл отправки с таймером остановки/повтора.
Получение и локальный буфер (BPMN)
Схема BPMN Вписать − + На весь экран Скачать исходник Скачать SVG
Загрузка схемы…
Исходник схемы <?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_report_receive" targetNamespace="urn:sharix:knowledge:handlers">
<bpmn:process id="inbox_Process_inbox" name="open_report_handler: входящий callback и локальный буфер" isExecutable="false">
<bpmn:documentation>{"notes": ["BPMN isExecutable=false; typed operation aggregate_handler_reports из Blockly, Python сверён канонически.", "Буфер reports — память под lock, не durable outbox.", "Успешный send-call не равен доставке/прочтению; повтор после неизвестного результата может дублировать отчёт."], "sources": [{"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "handlers/open_report_handler.py", "repository": "sharix-open-handlers-examples", "sha256": "9607c7f5b6f91632027095f5b3ebf64961ff8fd9c4624e80b5a39c6742ce61d5"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "generated_handler_blocks/open_report_handler.json", "repository": "sharix-open-handlers-examples", "sha256": "b4c158eca9cc5e7fc671629a01d367edcd351ecdbce446544fd6a1d103f0323c"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "catalog.py", "repository": "sharix-open-handlers-examples", "sha256": "e3dcb72b7e620e2837adacd7af053f35b04cdff475789496d9c114e17d57dfde"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "first_party_operations/open_report_handler.py", "repository": "sharix-open-handlers-examples", "sha256": "2caef84ded62efccbc6771b62e66e849b961ebcc62586083ea8f26f5cb918237"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "config.py", "repository": "sharix-open-handlers-examples", "sha256": "7b23db9ed283d3bbc3cf2cfbd5d41c629f6be507c3ca38c611572d08a75743a4"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "first_party_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "7f4dc6607ad62f3150c58de6341b758ebe97f81bd936c9ec033b7d86fed44640"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/first_party_handler_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "fb5ee98561b029de9291e73c2a08205cc9fc2b7b239abffe4a4bb24f56093bdb"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "main.py", "repository": "sharix-open-handlers-backend", "sha256": "66b7df94fedbec931ed4cb2d4e1afd9cf0386e14ed0932e22b15eb301cd792d7"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "runtime_provider.py", "repository": "sharix-open-handlers-backend", "sha256": "c311d3b0721d18d8eccc9cd7fba155108bf285df3002507ee915ee73b797a2f4"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/JabberBot.py", "repository": "sharix-open-handlers-backend", "sha256": "7fbd4d1f41bc867109b73dbb632efcfae920269a1aaf6da0e577cc70838820f1"}]}</bpmn:documentation>
<bpmn:laneSet id="inbox_Lanes_inbox">
<bpmn:lane id="inbox_Lane_worker" name="XMPP callback, независимо от цикла отправки">
<bpmn:flowNodeRef>inbox_start</bpmn:flowNodeRef>
<bpmn:flowNodeRef>inbox_type</bpmn:flowNodeRef>
<bpmn:flowNodeRef>inbox_allow</bpmn:flowNodeRef>
<bpmn:flowNodeRef>inbox_append</bpmn:flowNodeRef>
<bpmn:flowNodeRef>inbox_end</bpmn:flowNodeRef>
<bpmn:flowNodeRef>inbox_ignore</bpmn:flowNodeRef>
</bpmn:lane>
</bpmn:laneSet>
<bpmn:startEvent id="inbox_start" name="Получено XMPP сообщение">
<bpmn:outgoing>inbox_Flow_0</bpmn:outgoing>
<bpmn:messageEventDefinition />
</bpmn:startEvent>
<bpmn:exclusiveGateway id="inbox_type" name="chat/normal?">
<bpmn:incoming>inbox_Flow_0</bpmn:incoming>
<bpmn:outgoing>inbox_Flow_1</bpmn:outgoing>
<bpmn:outgoing>inbox_Flow_2</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:exclusiveGateway id="inbox_allow" name="Bare sender в listen_to, body не None?">
<bpmn:incoming>inbox_Flow_1</bpmn:incoming>
<bpmn:outgoing>inbox_Flow_3</bpmn:outgoing>
<bpmn:outgoing>inbox_Flow_5</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:serviceTask id="inbox_append" name="Lock; append text к списку sender JID">
<bpmn:incoming>inbox_Flow_3</bpmn:incoming>
<bpmn:outgoing>inbox_Flow_4</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:endEvent id="inbox_end" name="Сообщение в памяти; лог recorded">
<bpmn:incoming>inbox_Flow_4</bpmn:incoming>
</bpmn:endEvent>
<bpmn:endEvent id="inbox_ignore" name="Игнорировать">
<bpmn:incoming>inbox_Flow_2</bpmn:incoming>
<bpmn:incoming>inbox_Flow_5</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="inbox_Flow_0" sourceRef="inbox_start" targetRef="inbox_type" name="" />
<bpmn:sequenceFlow id="inbox_Flow_1" sourceRef="inbox_type" targetRef="inbox_allow" name="Да" />
<bpmn:sequenceFlow id="inbox_Flow_2" sourceRef="inbox_type" targetRef="inbox_ignore" name="Нет" />
<bpmn:sequenceFlow id="inbox_Flow_3" sourceRef="inbox_allow" targetRef="inbox_append" name="Да" />
<bpmn:sequenceFlow id="inbox_Flow_4" sourceRef="inbox_append" targetRef="inbox_end" name="" />
<bpmn:sequenceFlow id="inbox_Flow_5" sourceRef="inbox_allow" targetRef="inbox_ignore" name="Нет" />
</bpmn:process>
<bpmn:collaboration id="Collaboration_report_receive">
<bpmn:participant id="inbox_Pool_inbox" name="open_report_handler: входящий callback и локальный буфер" processRef="inbox_Process_inbox" />
<bpmn:participant id="senders_Participant" name="Разрешённые handlers из каталога и технический отправитель; server/domain из EJ_HOST" />
<bpmn:messageFlow id="Message_report_receive_0" sourceRef="senders_Participant" targetRef="inbox_start" name="Текст отчёта — данные; callback не исполняет команды" />
</bpmn:collaboration>
<bpmndi:BPMNDiagram id="Diagram_report_receive">
<bpmndi:BPMNPlane id="Plane_report_receive" bpmnElement="Collaboration_report_receive">
<bpmndi:BPMNShape id="inbox_DI_Pool_inbox" bpmnElement="inbox_Pool_inbox" isHorizontal="true">
<dc:Bounds x="0.0" y="0.0" width="1530" height="440" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="inbox_DI_Lane_worker" bpmnElement="inbox_Lane_worker" isHorizontal="true">
<dc:Bounds x="30.0" y="0.0" width="1500" height="440" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="inbox_DI_start" bpmnElement="inbox_start">
<dc:Bounds x="132.0" y="82.0" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="inbox_DI_type" bpmnElement="inbox_type">
<dc:Bounds x="415.0" y="75.0" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="inbox_DI_allow" bpmnElement="inbox_allow">
<dc:Bounds x="725.0" y="75.0" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="inbox_DI_append" bpmnElement="inbox_append">
<dc:Bounds x="985.0" y="65.0" width="150" height="70" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="inbox_DI_end" bpmnElement="inbox_end">
<dc:Bounds x="1352.0" y="82.0" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="inbox_DI_ignore" bpmnElement="inbox_ignore">
<dc:Bounds x="422.0" y="322.0" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="inbox_DI_Flow_0" bpmnElement="inbox_Flow_0">
<di:waypoint x="168.0" y="100.0" />
<di:waypoint x="415.0" y="100.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="inbox_DI_Flow_1" bpmnElement="inbox_Flow_1">
<di:waypoint x="465.0" y="100.0" />
<di:waypoint x="725.0" y="100.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="inbox_DI_Flow_2" bpmnElement="inbox_Flow_2">
<di:waypoint x="440.0" y="125.0" />
<di:waypoint x="440.0" y="322.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="inbox_DI_Flow_3" bpmnElement="inbox_Flow_3">
<di:waypoint x="775.0" y="100.0" />
<di:waypoint x="985.0" y="100.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="inbox_DI_Flow_4" bpmnElement="inbox_Flow_4">
<di:waypoint x="1135.0" y="100.0" />
<di:waypoint x="1352.0" y="100.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="inbox_DI_Flow_5" bpmnElement="inbox_Flow_5">
<di:waypoint x="775.0" y="100.0" />
<di:waypoint x="870.0" y="100.0" />
<di:waypoint x="920.0" y="100.0" />
<di:waypoint x="920.0" y="270.0" />
<di:waypoint x="440.0" y="270.0" />
<di:waypoint x="440.0" y="322.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="DI_senders_Participant" bpmnElement="senders_Participant" isHorizontal="true">
<dc:Bounds x="0" y="650" width="1530" height="130" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="DI_Message_report_receive_0" bpmnElement="Message_report_receive_0">
<di:waypoint x="1200" y="650" />
<di:waypoint x="1450" y="650" />
<di:waypoint x="1450" y="30" />
<di:waypoint x="150" y="30" />
<di:waypoint x="150" y="82" />
<bpmndi:BPMNLabel>
<dc:Bounds x="1000" y="530" width="260" height="42" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions> Для просмотра схемы включите JavaScript. Исходник доступен выше.
Входной фильтр:
Пропустить только chat/normal.
Отбросить resource и получить bare JID.
Сверить его с listen_to: handler ids каталога без самого open_report_handler и технический UID 5101 в EJ_HOST.
Пропустить body, отличный от None.
Каталог может включать материализованные contributions установки. Произвольный JID не становится разрешённым из-за слова «ошибка» в тексте.
После допуска сообщения:
Под lock добавить строку в список отправителя.
Записать лог recorded.
Позднее включить строку в snapshot сводки.
Callback не разбирает severity/JSON, не выполняет команды и не меняет Ticket. Event-id дедупликации нет; повторная доставка может стать ещё одной строкой.
Буфер — process memory. Он не хранится в БД или durable outbox; рестарт способен потерять неотправленное. Лог recorded подтверждает запись в локальную память текущего процесса, не пересылку получателю.
Сводка, пересылка и интервал (BPMN)
Схема BPMN Вписать − + На весь экран Скачать исходник Скачать SVG
Загрузка схемы…
Исходник схемы <?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" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_report_cycle" targetNamespace="urn:sharix:knowledge:handlers">
<bpmn:process id="aggregate_Process_aggregate" name="Агрегатор: снять snapshot, отправить, восстановить при полном неуспехе" isExecutable="false">
<bpmn:documentation>{"notes": ["BPMN isExecutable=false; typed operation aggregate_handler_reports из Blockly, Python сверён канонически.", "Буфер reports — память под lock, не durable outbox.", "Успешный send-call не равен доставке/прочтению; повтор после неизвестного результата может дублировать отчёт."], "sources": [{"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "handlers/open_report_handler.py", "repository": "sharix-open-handlers-examples", "sha256": "9607c7f5b6f91632027095f5b3ebf64961ff8fd9c4624e80b5a39c6742ce61d5"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "generated_handler_blocks/open_report_handler.json", "repository": "sharix-open-handlers-examples", "sha256": "b4c158eca9cc5e7fc671629a01d367edcd351ecdbce446544fd6a1d103f0323c"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "catalog.py", "repository": "sharix-open-handlers-examples", "sha256": "e3dcb72b7e620e2837adacd7af053f35b04cdff475789496d9c114e17d57dfde"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "first_party_operations/open_report_handler.py", "repository": "sharix-open-handlers-examples", "sha256": "2caef84ded62efccbc6771b62e66e849b961ebcc62586083ea8f26f5cb918237"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "config.py", "repository": "sharix-open-handlers-examples", "sha256": "7b23db9ed283d3bbc3cf2cfbd5d41c629f6be507c3ca38c611572d08a75743a4"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "first_party_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "7f4dc6607ad62f3150c58de6341b758ebe97f81bd936c9ec033b7d86fed44640"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/first_party_handler_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "fb5ee98561b029de9291e73c2a08205cc9fc2b7b239abffe4a4bb24f56093bdb"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "main.py", "repository": "sharix-open-handlers-backend", "sha256": "66b7df94fedbec931ed4cb2d4e1afd9cf0386e14ed0932e22b15eb301cd792d7"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "runtime_provider.py", "repository": "sharix-open-handlers-backend", "sha256": "c311d3b0721d18d8eccc9cd7fba155108bf285df3002507ee915ee73b797a2f4"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/JabberBot.py", "repository": "sharix-open-handlers-backend", "sha256": "7fbd4d1f41bc867109b73dbb632efcfae920269a1aaf6da0e577cc70838820f1"}]}</bpmn:documentation>
<bpmn:laneSet id="aggregate_Lanes_aggregate">
<bpmn:lane id="aggregate_Lane_worker" name="После общего XMPP connection preflight">
<bpmn:flowNodeRef>aggregate_start</bpmn:flowNodeRef>
<bpmn:flowNodeRef>aggregate_stop</bpmn:flowNodeRef>
<bpmn:flowNodeRef>aggregate_exit</bpmn:flowNodeRef>
<bpmn:flowNodeRef>aggregate_snapshot</bpmn:flowNodeRef>
<bpmn:flowNodeRef>aggregate_empty</bpmn:flowNodeRef>
<bpmn:flowNodeRef>aggregate_format</bpmn:flowNodeRef>
<bpmn:flowNodeRef>aggregate_send</bpmn:flowNodeRef>
<bpmn:flowNodeRef>aggregate_delivered</bpmn:flowNodeRef>
<bpmn:flowNodeRef>aggregate_restore</bpmn:flowNodeRef>
<bpmn:flowNodeRef>aggregate_wait</bpmn:flowNodeRef>
<bpmn:flowNodeRef>aggregate_repeat</bpmn:flowNodeRef>
</bpmn:lane>
</bpmn:laneSet>
<bpmn:startEvent id="aggregate_start" name="start_handler">
<bpmn:outgoing>aggregate_Flow_0</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:exclusiveGateway id="aggregate_stop" name="exit_event установлен?">
<bpmn:incoming>aggregate_Flow_0</bpmn:incoming>
<bpmn:outgoing>aggregate_Flow_1</bpmn:outgoing>
<bpmn:outgoing>aggregate_Flow_2</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:endEvent id="aggregate_exit" name="Завершить цикл, launcher закрывает XMPP">
<bpmn:incoming>aggregate_Flow_1</bpmn:incoming>
</bpmn:endEvent>
<bpmn:serviceTask id="aggregate_snapshot" name="Lock: скопировать reports и очистить буфер">
<bpmn:incoming>aggregate_Flow_2</bpmn:incoming>
<bpmn:outgoing>aggregate_Flow_3</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:exclusiveGateway id="aggregate_empty" name="Snapshot непустой?">
<bpmn:incoming>aggregate_Flow_3</bpmn:incoming>
<bpmn:outgoing>aggregate_Flow_4</bpmn:outgoing>
<bpmn:outgoing>aggregate_Flow_7</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:serviceTask id="aggregate_format" name="Собрать текст по JID и сообщениям">
<bpmn:incoming>aggregate_Flow_4</bpmn:incoming>
<bpmn:outgoing>aggregate_Flow_5</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:sendTask id="aggregate_send" name="Попытаться отправить всем proceed_to, кроме себя">
<bpmn:incoming>aggregate_Flow_5</bpmn:incoming>
<bpmn:outgoing>aggregate_Flow_6</bpmn:outgoing>
</bpmn:sendTask>
<bpmn:exclusiveGateway id="aggregate_delivered" name="Хотя бы один send-call без исключения?">
<bpmn:incoming>aggregate_Flow_6</bpmn:incoming>
<bpmn:outgoing>aggregate_Flow_8</bpmn:outgoing>
<bpmn:outgoing>aggregate_Flow_10</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:serviceTask id="aggregate_restore" name="Нет: вернуть snapshot в начало текущих списков под lock">
<bpmn:incoming>aggregate_Flow_8</bpmn:incoming>
<bpmn:outgoing>aggregate_Flow_9</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:intermediateCatchEvent id="aggregate_wait" name="exit_event.wait(INTERVAL_TIME_REPORT)">
<bpmn:incoming>aggregate_Flow_7</bpmn:incoming>
<bpmn:incoming>aggregate_Flow_9</bpmn:incoming>
<bpmn:incoming>aggregate_Flow_10</bpmn:incoming>
<bpmn:outgoing>aggregate_Flow_11</bpmn:outgoing>
<bpmn:timerEventDefinition>
<bpmn:timeDuration xsi:type="bpmn:tFormalExpression">interval_time_report</bpmn:timeDuration>
</bpmn:timerEventDefinition>
</bpmn:intermediateCatchEvent>
<bpmn:endEvent id="aggregate_repeat" name="Проверить stop и повторить цикл">
<bpmn:incoming>aggregate_Flow_11</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="aggregate_Flow_0" sourceRef="aggregate_start" targetRef="aggregate_stop" name="" />
<bpmn:sequenceFlow id="aggregate_Flow_1" sourceRef="aggregate_stop" targetRef="aggregate_exit" name="Да" />
<bpmn:sequenceFlow id="aggregate_Flow_2" sourceRef="aggregate_stop" targetRef="aggregate_snapshot" name="Нет" />
<bpmn:sequenceFlow id="aggregate_Flow_3" sourceRef="aggregate_snapshot" targetRef="aggregate_empty" name="" />
<bpmn:sequenceFlow id="aggregate_Flow_4" sourceRef="aggregate_empty" targetRef="aggregate_format" name="Да" />
<bpmn:sequenceFlow id="aggregate_Flow_5" sourceRef="aggregate_format" targetRef="aggregate_send" name="" />
<bpmn:sequenceFlow id="aggregate_Flow_6" sourceRef="aggregate_send" targetRef="aggregate_delivered" name="" />
<bpmn:sequenceFlow id="aggregate_Flow_7" sourceRef="aggregate_empty" targetRef="aggregate_wait" name="Нет" />
<bpmn:sequenceFlow id="aggregate_Flow_8" sourceRef="aggregate_delivered" targetRef="aggregate_restore" name="Нет" />
<bpmn:sequenceFlow id="aggregate_Flow_9" sourceRef="aggregate_restore" targetRef="aggregate_wait" name="" />
<bpmn:sequenceFlow id="aggregate_Flow_10" sourceRef="aggregate_delivered" targetRef="aggregate_wait" name="Да" />
<bpmn:sequenceFlow id="aggregate_Flow_11" sourceRef="aggregate_wait" targetRef="aggregate_repeat" name="" />
</bpmn:process>
<bpmn:collaboration id="Collaboration_report_cycle">
<bpmn:participant id="aggregate_Pool_aggregate" name="Агрегатор: снять snapshot, отправить, восстановить при полном неуспехе" processRef="aggregate_Process_aggregate" />
<bpmn:participant id="recipient_Participant" name="Технический получатель отчётов: фиксированный в operation UID, не staff-группа по Ticket" />
<bpmn:messageFlow id="Message_report_cycle_0" sourceRef="aggregate_send" targetRef="recipient_Participant" name="XMPP chat; нет receipt, редакторской реакции или durable квитанции" />
</bpmn:collaboration>
<bpmndi:BPMNDiagram id="Diagram_report_cycle">
<bpmndi:BPMNPlane id="Plane_report_cycle" bpmnElement="Collaboration_report_cycle">
<bpmndi:BPMNShape id="aggregate_DI_Pool_aggregate" bpmnElement="aggregate_Pool_aggregate" isHorizontal="true">
<dc:Bounds x="0.0" y="0.0" width="2150" height="680" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="aggregate_DI_Lane_worker" bpmnElement="aggregate_Lane_worker" isHorizontal="true">
<dc:Bounds x="30.0" y="0.0" width="2120" height="680" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="aggregate_DI_start" bpmnElement="aggregate_start">
<dc:Bounds x="132.0" y="82.0" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="aggregate_DI_stop" bpmnElement="aggregate_stop">
<dc:Bounds x="415.0" y="75.0" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="aggregate_DI_exit" bpmnElement="aggregate_exit">
<dc:Bounds x="422.0" y="322.0" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="aggregate_DI_snapshot" bpmnElement="aggregate_snapshot">
<dc:Bounds x="675.0" y="65.0" width="150" height="70" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="aggregate_DI_empty" bpmnElement="aggregate_empty">
<dc:Bounds x="1035.0" y="75.0" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="aggregate_DI_format" bpmnElement="aggregate_format">
<dc:Bounds x="1295.0" y="65.0" width="150" height="70" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="aggregate_DI_send" bpmnElement="aggregate_send">
<dc:Bounds x="1605.0" y="65.0" width="150" height="70" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="aggregate_DI_delivered" bpmnElement="aggregate_delivered">
<dc:Bounds x="1965.0" y="75.0" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="aggregate_DI_restore" bpmnElement="aggregate_restore">
<dc:Bounds x="1915.0" y="305.0" width="150" height="70" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="aggregate_DI_wait" bpmnElement="aggregate_wait">
<dc:Bounds x="1352.0" y="562.0" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="aggregate_DI_repeat" bpmnElement="aggregate_repeat">
<dc:Bounds x="1662.0" y="562.0" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="aggregate_DI_Flow_0" bpmnElement="aggregate_Flow_0">
<di:waypoint x="168.0" y="100.0" />
<di:waypoint x="415.0" y="100.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="aggregate_DI_Flow_1" bpmnElement="aggregate_Flow_1">
<di:waypoint x="440.0" y="125.0" />
<di:waypoint x="440.0" y="322.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="aggregate_DI_Flow_2" bpmnElement="aggregate_Flow_2">
<di:waypoint x="465.0" y="100.0" />
<di:waypoint x="675.0" y="100.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="aggregate_DI_Flow_3" bpmnElement="aggregate_Flow_3">
<di:waypoint x="825.0" y="100.0" />
<di:waypoint x="1035.0" y="100.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="aggregate_DI_Flow_4" bpmnElement="aggregate_Flow_4">
<di:waypoint x="1085.0" y="100.0" />
<di:waypoint x="1295.0" y="100.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="aggregate_DI_Flow_5" bpmnElement="aggregate_Flow_5">
<di:waypoint x="1445.0" y="100.0" />
<di:waypoint x="1605.0" y="100.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="aggregate_DI_Flow_6" bpmnElement="aggregate_Flow_6">
<di:waypoint x="1755.0" y="100.0" />
<di:waypoint x="1965.0" y="100.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="aggregate_DI_Flow_7" bpmnElement="aggregate_Flow_7">
<di:waypoint x="1085.0" y="100.0" />
<di:waypoint x="1180.0" y="100.0" />
<di:waypoint x="1230.0" y="100.0" />
<di:waypoint x="1230.0" y="510.0" />
<di:waypoint x="1370.0" y="510.0" />
<di:waypoint x="1370.0" y="562.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="aggregate_DI_Flow_8" bpmnElement="aggregate_Flow_8">
<di:waypoint x="1990.0" y="125.0" />
<di:waypoint x="1990.0" y="305.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="aggregate_DI_Flow_9" bpmnElement="aggregate_Flow_9">
<di:waypoint x="2065.0" y="340.0" />
<di:waypoint x="2110.0" y="340.0" />
<di:waypoint x="2170.0" y="340.0" />
<di:waypoint x="2170.0" y="510.0" />
<di:waypoint x="1370.0" y="510.0" />
<di:waypoint x="1370.0" y="562.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="aggregate_DI_Flow_10" bpmnElement="aggregate_Flow_10">
<di:waypoint x="2015.0" y="100.0" />
<di:waypoint x="2110.0" y="100.0" />
<di:waypoint x="2170.0" y="100.0" />
<di:waypoint x="2170.0" y="510.0" />
<di:waypoint x="1370.0" y="510.0" />
<di:waypoint x="1370.0" y="562.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="aggregate_DI_Flow_11" bpmnElement="aggregate_Flow_11">
<di:waypoint x="1388.0" y="580.0" />
<di:waypoint x="1662.0" y="580.0" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="DI_recipient_Participant" bpmnElement="recipient_Participant" isHorizontal="true">
<dc:Bounds x="0" y="900" width="2300" height="130" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="DI_Message_report_cycle_0" bpmnElement="Message_report_cycle_0">
<di:waypoint x="1680" y="135" />
<di:waypoint x="1680" y="800" />
<di:waypoint x="1200" y="800" />
<di:waypoint x="1200" y="900" />
<bpmndi:BPMNLabel>
<dc:Bounds x="1300" y="760" width="260" height="42" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions> Для просмотра схемы включите JavaScript. Исходник доступен выше.
Итерация отправки после XMPP connection preflight:
Проверить exit_event.
Под lock снять копию reports и очистить исходный буфер.
Пока snapshot отправляется, принимать новые сообщения в отдельный текущий буфер.
При непустом snapshot сформировать и отправить сводку.
Первая итерация начинается сразу; полный INTERVAL_TIME_REPORT до неё не ожидается.
Отправка сводки:
формат — JID и списки сообщений;
транспорт — XMPP chat;
получатели — proceed_to без собственного JID;
в текущей operation единственный адресат — фиксированный 5101@EJ_HOST.
Этот адресат не вычисляется из METASERVICE-ADMIN/staff, Ticket.group или авторов сообщений. Изменение получателя требует правки соответствующего контракта/конфигурации.
Результат send обрабатывается так:
хотя бы один вызов без исключения устанавливает delivered=true;
если таких вызовов нет, snapshot под lock возвращается в начало текущих списков перед новыми сообщениями;
при хотя бы одном успешном вызове snapshot для остальных адресатов не восстанавливается.
Это не receipt и не прочтение. Сейчас адресат один; алгоритм не является индивидуальным durable retry для будущего списка получателей.
После итерации выполняется exit_event.wait(INTERVAL_TIME_REPORT). Это прерываемое ожидание: сигнал остановки не обязан ждать весь интервал. Затем цикл повторяется. Пустой snapshot не порождает сообщение «No reports available»: отправка в start_handler выполняется только при непустом наборе, хотя formatter имеет такую строку для отдельного вызова.
Ошибки и границы доставки
Исключение send отдельному адресату логируется, цикл продолжает остальных. Полный неуспех восстанавливает snapshot в память и ждёт следующей итерации. Неопределённый сетевой результат способен привести к повтору сводки: код не реализует exactly-once или durable квитанцию по сообщению.
Ошибки вне обработанного send-контекста могут завершить operation и перейти к общей политике рестартов launcher. Восстановленный в память snapshot всё равно не защищён от рестарта процесса. Нормальная остановка прекращает цикл, после чего launcher закрывает XMPP. Здесь нет отдельной обязательной финальной flush-сводки при остановке.
Диагностика
Порядок диагностики:
Проверить тип сообщения и bare JID по актуальному allowlist.
Подтвердить добавление в reports, учитывая время жизни процесса.
Найти снятый snapshot и момент итерации INTERVAL_TIME_REPORT.
Проверить фиксированного адресата и исход send.
Отдельно подтвердить фактическое получение.
Лог recorded относится к локальной памяти, а наличие строк в файловом журнале другого handler не доказывает отправки агрегатору.
Успешный запуск общего runtime ещё не доказывает, что каждый handler отправляет отчёты в этот JID. Наличие сообщений в обычных файловых логах также не означает, что они были отправлены агрегатору. Для надёжного архива нужны предусмотренные системы журналирования; этот handler не заменяет их.
Проверенные источники
Git SHA/SHA-256 и модели закреплены в metadata/BPMN; упаковка повторно сверяет файлы. Handler и тестовые сообщения при анализе не запускались; секретные runtime config не читались. Общий автоматический hook Wiki пока не завершён.
Связанные материалы