Инструменты пользователя

Инструменты сайта


sharixopen:knowledge:scenarios:repository_update_watch

Это старая версия документа!


Монитор обновлений repository_update_watch

Область: публичный контракт ShariX Open/shared Sysadmin. Обработчик: repository_update_watch, module contribution sharix_sysadmin, без типа/статуса Ticket. Проверено 29.10.2026 по свежему Sysadmin Git и закреплённым shared contracts. Python побайтово совпадает с каноническим optional Blockly renderer. Включённость и выбранная версия установки проверяются отдельно.

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

Обработчик обновляет сведения о deployment/upstream Git-состоянии управляемых установленных repositories. Он не выполняет установку новой версии, git pull/merge, переключение рабочих веток, migrations или restart сервиса. Найденное обновление — evidence для решения оператора, не само применение обновления.

Contribution по умолчанию disabled, version builtin; это optional-возможность, не обязательная зависимость обычной установки. В текущем опубликованном модуле blockly_editable=true и есть module-owned workspace/generated Python. Blockly содержит typed operation repository_updates.refresh_public с интервалом 21600 секунд. Исполняется сгенерированный Python, а не Blockly JSON; BPMN isExecutable=false.

Интервал, API и повтор (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" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_repository_watch_cycle" targetNamespace="urn:sharix:knowledge:handlers">
  <bpmn:process id="watch_Process_watch" name="repository_update_watch: process-local cadence и scoped operation" isExecutable="false">
    <bpmn:documentation>{"notes": ["Описательная BPMN, isExecutable=false; optional Blockly Python побайтово проверен.", "requires_xmpp=False; Django-free process вызывает объявленную backend operation.", "21600 секунд — интервал после успешного завершения; первый запуск сразу, память теряется при рестарте.", "Проверка evidence не выполняет install/pull/merge/restart."], "sources": [{"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "handler_contributions.py", "repository": "sharix-open-sysadmin", "sha256": "96182d34efae36a2abf6b0ea81f61690f4d16d422a8e387a28ebf221eb520d82"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "handlers/repository_update_watch.py", "repository": "sharix-open-sysadmin", "sha256": "4dae269db5f8a96b1d74aece1faae3a68a0378569c48a47535c012a4bf4de6d3"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "handlers/blockly/repository_update_watch.json", "repository": "sharix-open-sysadmin", "sha256": "adaf5e5633e81617b525906621c226680744698187a3c4e31cae56f4fe5b71db"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "services/repository_update_handler.py", "repository": "sharix-open-sysadmin", "sha256": "2e9ba086f017a1f4ba3c11b59ef0362d3a2039fb177cd54767f4b506eb2ae6b2"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "services/repository_updates.py", "repository": "sharix-open-sysadmin", "sha256": "3f36c0ec4efdb07377be0e9b0c04d7557982f6c8ea6c7c98b1500053bc8e0689"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "services/source_provenance.py", "repository": "sharix-open-sysadmin", "sha256": "b89683e9a17d5d0202290f40cfa78d57aa6751691fb2a6ed0af06e2da75e477f"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "optional_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "139b8250a40292fc756dca9f415ce2a474644bc4ebfd69270b71b29ea3c7047a"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/optional_handler_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "de2c71b8b35bdd3ff81827a597c24cd87eb35441251012911d1ab2baacf38639"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/requesters.py", "repository": "sharix-open-handlers-backend", "sha256": "965a348cea477722bbaf4c8c6b06023eb238b71664a332f7ea8c90bfe72ea808"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "main.py", "repository": "sharix-open-handlers-backend", "sha256": "66b7df94fedbec931ed4cb2d4e1afd9cf0386e14ed0932e22b15eb301cd792d7"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/handler_optional.py", "repository": "sharix-open-backend", "sha256": "0ff88578f68777a67fcf9624fabb580f829f3acbce041816b0c835495d6a3e2d"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/optional_handler_runtime.py", "repository": "sharix-open-backend", "sha256": "fd4fd4cf0e2fd4b9d6c2a658f9acc7784e819306540a0995660e114a8e7bca72"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/handler_contributions.py", "repository": "sharix-open-backend", "sha256": "98b32d6f8b27eb51dce8f767d5c45a42fd08cf5f8d749eff95da286183dda00f"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "urls.py", "repository": "sharix-open-backend", "sha256": "61ffd2f0563291da2dcc9488a5443cb5e2c0395108740a6700034cfd90548158"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/handler_auth.py", "repository": "sharix-open-tickets", "sha256": "ac76dc45aaa9742e5e1da0b98aae914bb2e287048d33e97b4a245a6997bc8425"}]}</bpmn:documentation>
    <bpmn:laneSet id="watch_Lanes_watch">
      <bpmn:lane id="watch_Lane_worker" name="Без XMPP, без Ticket-статуса">
        <bpmn:flowNodeRef>watch_start</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>watch_due</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>watch_call</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>watch_ok</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>watch_save</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>watch_wait</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>watch_repeat</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>watch_error</bpmn:flowNodeRef>
      </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:startEvent id="watch_start" name="Enabled handler, выбранный Python и identity preflight">
      <bpmn:outgoing>watch_Flow_0</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:exclusiveGateway id="watch_due" name="Первый проход или прошло 21600 s после успеха?">
      <bpmn:incoming>watch_Flow_0</bpmn:incoming>
      <bpmn:outgoing>watch_Flow_1</bpmn:outgoing>
      <bpmn:outgoing>watch_Flow_2</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:serviceTask id="watch_call" name="POST optional-operation: repository_updates.refresh_public">
      <bpmn:incoming>watch_Flow_1</bpmn:incoming>
      <bpmn:outgoing>watch_Flow_3</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:exclusiveGateway id="watch_ok" name="HTTP/JSON status=ok?">
      <bpmn:incoming>watch_Flow_3</bpmn:incoming>
      <bpmn:outgoing>watch_Flow_4</bpmn:outgoing>
      <bpmn:outgoing>watch_Flow_6</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:serviceTask id="watch_save" name="Запомнить monotonic completion time">
      <bpmn:incoming>watch_Flow_4</bpmn:incoming>
      <bpmn:outgoing>watch_Flow_5</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:intermediateCatchEvent id="watch_wait" name="exit_event.wait(INTERVAL_TIME)">
      <bpmn:incoming>watch_Flow_2</bpmn:incoming>
      <bpmn:incoming>watch_Flow_5</bpmn:incoming>
      <bpmn:incoming>watch_Flow_7</bpmn:incoming>
      <bpmn:outgoing>watch_Flow_8</bpmn:outgoing>
      <bpmn:timerEventDefinition>
        <bpmn:timeDuration xsi:type="bpmn:tFormalExpression">base_optional_handler_interval</bpmn:timeDuration>
      </bpmn:timerEventDefinition>
    </bpmn:intermediateCatchEvent>
    <bpmn:endEvent id="watch_repeat" name="Следующий проход либо остановка">
      <bpmn:incoming>watch_Flow_8</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:serviceTask id="watch_error" name="Ошибка: лог, не менять last_run">
      <bpmn:incoming>watch_Flow_6</bpmn:incoming>
      <bpmn:outgoing>watch_Flow_7</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:sequenceFlow id="watch_Flow_0" sourceRef="watch_start" targetRef="watch_due" name="" />
    <bpmn:sequenceFlow id="watch_Flow_1" sourceRef="watch_due" targetRef="watch_call" name="Да" />
    <bpmn:sequenceFlow id="watch_Flow_2" sourceRef="watch_due" targetRef="watch_wait" name="Нет" />
    <bpmn:sequenceFlow id="watch_Flow_3" sourceRef="watch_call" targetRef="watch_ok" name="" />
    <bpmn:sequenceFlow id="watch_Flow_4" sourceRef="watch_ok" targetRef="watch_save" name="Да" />
    <bpmn:sequenceFlow id="watch_Flow_5" sourceRef="watch_save" targetRef="watch_wait" name="" />
    <bpmn:sequenceFlow id="watch_Flow_6" sourceRef="watch_ok" targetRef="watch_error" name="Нет / исключение" />
    <bpmn:sequenceFlow id="watch_Flow_7" sourceRef="watch_error" targetRef="watch_wait" name="" />
    <bpmn:sequenceFlow id="watch_Flow_8" sourceRef="watch_wait" targetRef="watch_repeat" name="" />
  </bpmn:process>
  <bpmn:collaboration id="Collaboration_repository_watch_cycle">
    <bpmn:participant id="watch_Pool_watch" name="repository_update_watch: process-local cadence и scoped operation" processRef="watch_Process_watch" />
  </bpmn:collaboration>
  <bpmndi:BPMNDiagram id="Diagram_repository_watch_cycle">
    <bpmndi:BPMNPlane id="Plane_repository_watch_cycle" bpmnElement="Collaboration_repository_watch_cycle">
      <bpmndi:BPMNShape id="watch_DI_Pool_watch" bpmnElement="watch_Pool_watch" isHorizontal="true">
        <dc:Bounds x="0.0" y="0.0" width="1840" height="680" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="watch_DI_Lane_worker" bpmnElement="watch_Lane_worker" isHorizontal="true">
        <dc:Bounds x="30.0" y="0.0" width="1810" height="680" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="watch_DI_start" bpmnElement="watch_start">
        <dc:Bounds x="132.0" y="82.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="watch_DI_due" bpmnElement="watch_due">
        <dc:Bounds x="415.0" y="75.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="watch_DI_call" bpmnElement="watch_call">
        <dc:Bounds x="675.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="watch_DI_ok" bpmnElement="watch_ok">
        <dc:Bounds x="1035.0" y="75.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="watch_DI_save" bpmnElement="watch_save">
        <dc:Bounds x="1295.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="watch_DI_wait" bpmnElement="watch_wait">
        <dc:Bounds x="1352.0" y="322.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="watch_DI_repeat" bpmnElement="watch_repeat">
        <dc:Bounds x="1662.0" y="322.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="watch_DI_error" bpmnElement="watch_error">
        <dc:Bounds x="985.0" y="545.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="watch_DI_Flow_0" bpmnElement="watch_Flow_0">
        <di:waypoint x="168.0" y="100.0" />
        <di:waypoint x="415.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="watch_DI_Flow_1" bpmnElement="watch_Flow_1">
        <di:waypoint x="465.0" y="100.0" />
        <di:waypoint x="675.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="watch_DI_Flow_2" bpmnElement="watch_Flow_2">
        <di:waypoint x="465.0" y="100.0" />
        <di:waypoint x="560.0" y="100.0" />
        <di:waypoint x="600.0" y="100.0" />
        <di:waypoint x="600.0" y="270.0" />
        <di:waypoint x="1370.0" y="270.0" />
        <di:waypoint x="1370.0" y="322.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="watch_DI_Flow_3" bpmnElement="watch_Flow_3">
        <di:waypoint x="825.0" y="100.0" />
        <di:waypoint x="1035.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="watch_DI_Flow_4" bpmnElement="watch_Flow_4">
        <di:waypoint x="1085.0" y="100.0" />
        <di:waypoint x="1295.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="watch_DI_Flow_5" bpmnElement="watch_Flow_5">
        <di:waypoint x="1370.0" y="135.0" />
        <di:waypoint x="1370.0" y="322.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="watch_DI_Flow_6" bpmnElement="watch_Flow_6">
        <di:waypoint x="1060.0" y="125.0" />
        <di:waypoint x="1060.0" y="545.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="watch_DI_Flow_7" bpmnElement="watch_Flow_7">
        <di:waypoint x="1135.0" y="580.0" />
        <di:waypoint x="1180.0" y="580.0" />
        <di:waypoint x="1250.0" y="580.0" />
        <di:waypoint x="1250.0" y="270.0" />
        <di:waypoint x="1370.0" y="270.0" />
        <di:waypoint x="1370.0" y="322.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="watch_DI_Flow_8" bpmnElement="watch_Flow_8">
        <di:waypoint x="1388.0" y="340.0" />
        <di:waypoint x="1662.0" y="340.0" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

OptionalHandlerContext не требует XMPP и выполняет общий identity preflight. Далее generated process_cycle вызывает run_operation_every с явным интервалом 21600 секунд (6 часов).

Первый проход выполняет operation сразу. После успешного HTTP/JSON status=ok запоминается monotonic-время завершения. До истечения интервала запрос пропускается. Таймер хранится в памяти: рестарт запускает первую проверку заново; это не постоянный календарный cron с фиксированным временем суток.

Внешний процессный цикл ждёт INTERVAL_TIME через exit_event.wait, то есть остановка прерывает ожидание. При исключении operation last_run не обновляется; ошибка логируется, следующий базовый цикл может повторить запрос. Но SOURCE_UNAVAILABLE или PRIVATE_AUTH_REQUIRED в успешно возвращённом evidence не являются исключением API: такой status=ok начинает обычный шестичасовой интервал.

Узкий backend endpoint проверяет HasHandlerAPIKey, identity, materialized contribution и наличие operation у этого handler, затем требует capability repository.update.watch. Callback берётся только из зарегистрированного contribution и исполняет только объявленный operation code. Бот не получает универсальный API выполнения команд из текста/payload.

Внешний Git и snapshot (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_repository_watch_evidence" targetNamespace="urn:sharix:knowledge:handlers">
  <bpmn:process id="api_Process_api" name="Backend + Sysadmin: только evidence управляемых установленных repositories" isExecutable="false">
    <bpmn:documentation>{"notes": ["Описательная BPMN, isExecutable=false; optional Blockly Python побайтово проверен.", "requires_xmpp=False; Django-free process вызывает объявленную backend operation.", "21600 секунд — интервал после успешного завершения; первый запуск сразу, память теряется при рестарте.", "Проверка evidence не выполняет install/pull/merge/restart."], "sources": [{"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "handler_contributions.py", "repository": "sharix-open-sysadmin", "sha256": "96182d34efae36a2abf6b0ea81f61690f4d16d422a8e387a28ebf221eb520d82"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "handlers/repository_update_watch.py", "repository": "sharix-open-sysadmin", "sha256": "4dae269db5f8a96b1d74aece1faae3a68a0378569c48a47535c012a4bf4de6d3"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "handlers/blockly/repository_update_watch.json", "repository": "sharix-open-sysadmin", "sha256": "adaf5e5633e81617b525906621c226680744698187a3c4e31cae56f4fe5b71db"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "services/repository_update_handler.py", "repository": "sharix-open-sysadmin", "sha256": "2e9ba086f017a1f4ba3c11b59ef0362d3a2039fb177cd54767f4b506eb2ae6b2"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "services/repository_updates.py", "repository": "sharix-open-sysadmin", "sha256": "3f36c0ec4efdb07377be0e9b0c04d7557982f6c8ea6c7c98b1500053bc8e0689"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "services/source_provenance.py", "repository": "sharix-open-sysadmin", "sha256": "b89683e9a17d5d0202290f40cfa78d57aa6751691fb2a6ed0af06e2da75e477f"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "optional_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "139b8250a40292fc756dca9f415ce2a474644bc4ebfd69270b71b29ea3c7047a"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/optional_handler_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "de2c71b8b35bdd3ff81827a597c24cd87eb35441251012911d1ab2baacf38639"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/requesters.py", "repository": "sharix-open-handlers-backend", "sha256": "965a348cea477722bbaf4c8c6b06023eb238b71664a332f7ea8c90bfe72ea808"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "main.py", "repository": "sharix-open-handlers-backend", "sha256": "66b7df94fedbec931ed4cb2d4e1afd9cf0386e14ed0932e22b15eb301cd792d7"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/handler_optional.py", "repository": "sharix-open-backend", "sha256": "0ff88578f68777a67fcf9624fabb580f829f3acbce041816b0c835495d6a3e2d"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/optional_handler_runtime.py", "repository": "sharix-open-backend", "sha256": "fd4fd4cf0e2fd4b9d6c2a658f9acc7784e819306540a0995660e114a8e7bca72"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/handler_contributions.py", "repository": "sharix-open-backend", "sha256": "98b32d6f8b27eb51dce8f767d5c45a42fd08cf5f8d749eff95da286183dda00f"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "urls.py", "repository": "sharix-open-backend", "sha256": "61ffd2f0563291da2dcc9488a5443cb5e2c0395108740a6700034cfd90548158"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/handler_auth.py", "repository": "sharix-open-tickets", "sha256": "ac76dc45aaa9742e5e1da0b98aae914bb2e287048d33e97b4a245a6997bc8425"}]}</bpmn:documentation>
    <bpmn:laneSet id="api_Lanes_api">
      <bpmn:lane id="api_Lane_worker" name="Identity/contribution/operation/capability gate">
        <bpmn:flowNodeRef>api_start</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>api_auth</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>api_inventory</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>api_visibility</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>api_private</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>api_git</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>api_cache</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>api_end</bpmn:flowNodeRef>
      </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:startEvent id="api_start" name="Optional operation request">
      <bpmn:outgoing>api_Flow_0</bpmn:outgoing>
      <bpmn:messageEventDefinition />
    </bpmn:startEvent>
    <bpmn:serviceTask id="api_auth" name="Проверить handler id, declared operation и repository.update.watch">
      <bpmn:incoming>api_Flow_0</bpmn:incoming>
      <bpmn:outgoing>api_Flow_1</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:serviceTask id="api_inventory" name="Собрать локальные installed source facts и цели deployment/upstream">
      <bpmn:incoming>api_Flow_1</bpmn:incoming>
      <bpmn:outgoing>api_Flow_2</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:exclusiveGateway id="api_visibility" name="Цель private?">
      <bpmn:incoming>api_Flow_2</bpmn:incoming>
      <bpmn:outgoing>api_Flow_3</bpmn:outgoing>
      <bpmn:outgoing>api_Flow_4</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:serviceTask id="api_private" name="Без запроса с credentials: PRIVATE_AUTH_REQUIRED">
      <bpmn:incoming>api_Flow_3</bpmn:incoming>
      <bpmn:outgoing>api_Flow_5</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:serviceTask id="api_git" name="Public ls-remote и bounded временная проверка ancestry">
      <bpmn:incoming>api_Flow_4</bpmn:incoming>
      <bpmn:outgoing>api_Flow_6</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:serviceTask id="api_cache" name="Сохранить snapshot evidence/summary; код не обновлять">
      <bpmn:incoming>api_Flow_5</bpmn:incoming>
      <bpmn:incoming>api_Flow_6</bpmn:incoming>
      <bpmn:outgoing>api_Flow_7</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:endEvent id="api_end" name="Operation status=ok может содержать unavailable/private states">
      <bpmn:incoming>api_Flow_7</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="api_Flow_0" sourceRef="api_start" targetRef="api_auth" name="" />
    <bpmn:sequenceFlow id="api_Flow_1" sourceRef="api_auth" targetRef="api_inventory" name="" />
    <bpmn:sequenceFlow id="api_Flow_2" sourceRef="api_inventory" targetRef="api_visibility" name="" />
    <bpmn:sequenceFlow id="api_Flow_3" sourceRef="api_visibility" targetRef="api_private" name="Да" />
    <bpmn:sequenceFlow id="api_Flow_4" sourceRef="api_visibility" targetRef="api_git" name="Нет" />
    <bpmn:sequenceFlow id="api_Flow_5" sourceRef="api_private" targetRef="api_cache" name="" />
    <bpmn:sequenceFlow id="api_Flow_6" sourceRef="api_git" targetRef="api_cache" name="" />
    <bpmn:sequenceFlow id="api_Flow_7" sourceRef="api_cache" targetRef="api_end" name="" />
  </bpmn:process>
  <bpmn:process id="view_Process_view" name="Страница Sysadmin: локальный snapshot + актуальные local facts, без Git network при обычном чтении" isExecutable="false">
    <bpmn:documentation>{"notes": ["Описательная BPMN, isExecutable=false; optional Blockly Python побайтово проверен.", "requires_xmpp=False; Django-free process вызывает объявленную backend operation.", "21600 секунд — интервал после успешного завершения; первый запуск сразу, память теряется при рестарте.", "Проверка evidence не выполняет install/pull/merge/restart."], "sources": [{"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "handler_contributions.py", "repository": "sharix-open-sysadmin", "sha256": "96182d34efae36a2abf6b0ea81f61690f4d16d422a8e387a28ebf221eb520d82"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "handlers/repository_update_watch.py", "repository": "sharix-open-sysadmin", "sha256": "4dae269db5f8a96b1d74aece1faae3a68a0378569c48a47535c012a4bf4de6d3"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "handlers/blockly/repository_update_watch.json", "repository": "sharix-open-sysadmin", "sha256": "adaf5e5633e81617b525906621c226680744698187a3c4e31cae56f4fe5b71db"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "services/repository_update_handler.py", "repository": "sharix-open-sysadmin", "sha256": "2e9ba086f017a1f4ba3c11b59ef0362d3a2039fb177cd54767f4b506eb2ae6b2"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "services/repository_updates.py", "repository": "sharix-open-sysadmin", "sha256": "3f36c0ec4efdb07377be0e9b0c04d7557982f6c8ea6c7c98b1500053bc8e0689"}, {"commit": "60e386e892793ffe37b8f0d842330c6e5635068e", "path": "services/source_provenance.py", "repository": "sharix-open-sysadmin", "sha256": "b89683e9a17d5d0202290f40cfa78d57aa6751691fb2a6ed0af06e2da75e477f"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "optional_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "139b8250a40292fc756dca9f415ce2a474644bc4ebfd69270b71b29ea3c7047a"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/optional_handler_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "de2c71b8b35bdd3ff81827a597c24cd87eb35441251012911d1ab2baacf38639"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/requesters.py", "repository": "sharix-open-handlers-backend", "sha256": "965a348cea477722bbaf4c8c6b06023eb238b71664a332f7ea8c90bfe72ea808"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "main.py", "repository": "sharix-open-handlers-backend", "sha256": "66b7df94fedbec931ed4cb2d4e1afd9cf0386e14ed0932e22b15eb301cd792d7"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/handler_optional.py", "repository": "sharix-open-backend", "sha256": "0ff88578f68777a67fcf9624fabb580f829f3acbce041816b0c835495d6a3e2d"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/optional_handler_runtime.py", "repository": "sharix-open-backend", "sha256": "fd4fd4cf0e2fd4b9d6c2a658f9acc7784e819306540a0995660e114a8e7bca72"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/handler_contributions.py", "repository": "sharix-open-backend", "sha256": "98b32d6f8b27eb51dce8f767d5c45a42fd08cf5f8d749eff95da286183dda00f"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "urls.py", "repository": "sharix-open-backend", "sha256": "61ffd2f0563291da2dcc9488a5443cb5e2c0395108740a6700034cfd90548158"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/handler_auth.py", "repository": "sharix-open-tickets", "sha256": "ac76dc45aaa9742e5e1da0b98aae914bb2e287048d33e97b4a245a6997bc8425"}]}</bpmn:documentation>
    <bpmn:laneSet id="view_Lanes_view">
      <bpmn:lane id="view_Lane_worker" name="Read path не запускает refresh автоматически">
        <bpmn:flowNodeRef>view_start</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>view_identity</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>view_current</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>view_show</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>view_stale</bpmn:flowNodeRef>
      </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:startEvent id="view_start" name="Открыть статус repositories">
      <bpmn:outgoing>view_Flow_0</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:serviceTask id="view_identity" name="Сверить identity snapshot с source/branch/commit/worktree">
      <bpmn:incoming>view_Flow_0</bpmn:incoming>
      <bpmn:outgoing>view_Flow_1</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:exclusiveGateway id="view_current" name="Snapshot соответствует?">
      <bpmn:incoming>view_Flow_1</bpmn:incoming>
      <bpmn:outgoing>view_Flow_2</bpmn:outgoing>
      <bpmn:outgoing>view_Flow_3</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:endEvent id="view_show" name="Да: показать сохранённое evidence">
      <bpmn:incoming>view_Flow_2</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:endEvent id="view_stale" name="Нет: не выдавать прежний CURRENT за актуальное">
      <bpmn:incoming>view_Flow_3</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="view_Flow_0" sourceRef="view_start" targetRef="view_identity" name="" />
    <bpmn:sequenceFlow id="view_Flow_1" sourceRef="view_identity" targetRef="view_current" name="" />
    <bpmn:sequenceFlow id="view_Flow_2" sourceRef="view_current" targetRef="view_show" name="Да" />
    <bpmn:sequenceFlow id="view_Flow_3" sourceRef="view_current" targetRef="view_stale" name="Нет" />
  </bpmn:process>
  <bpmn:collaboration id="Collaboration_repository_watch_evidence">
    <bpmn:participant id="api_Pool_api" name="Backend + Sysadmin: только evidence управляемых установленных repositories" processRef="api_Process_api" />
    <bpmn:participant id="git_Participant" name="Внешние публичные Git/source endpoints: факт опубликованного ref, не remote attestation" />
    <bpmn:participant id="view_Pool_view" name="Страница Sysadmin: локальный snapshot + актуальные local facts, без Git network при обычном чтении" processRef="view_Process_view" />
    <bpmn:messageFlow id="Message_repository_watch_evidence_0" sourceRef="api_git" targetRef="git_Participant" name="Anonymous read, no prompt/helper credentials" />
    <bpmn:messageFlow id="Message_repository_watch_evidence_1" sourceRef="api_cache" targetRef="view_identity" name="Нечувствительное evidence в managed snapshot" />
  </bpmn:collaboration>
  <bpmndi:BPMNDiagram id="Diagram_repository_watch_evidence">
    <bpmndi:BPMNPlane id="Plane_repository_watch_evidence" bpmnElement="Collaboration_repository_watch_evidence">
      <bpmndi:BPMNShape id="api_DI_Pool_api" bpmnElement="api_Pool_api" isHorizontal="true">
        <dc:Bounds x="0.0" y="0.0" width="2150" height="440" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="api_DI_Lane_worker" bpmnElement="api_Lane_worker" isHorizontal="true">
        <dc:Bounds x="30.0" y="0.0" width="2120" height="440" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="api_DI_start" bpmnElement="api_start">
        <dc:Bounds x="132.0" y="82.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="api_DI_auth" bpmnElement="api_auth">
        <dc:Bounds x="365.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="api_DI_inventory" bpmnElement="api_inventory">
        <dc:Bounds x="675.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="api_DI_visibility" bpmnElement="api_visibility">
        <dc:Bounds x="1035.0" y="75.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="api_DI_private" bpmnElement="api_private">
        <dc:Bounds x="985.0" y="305.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="api_DI_git" bpmnElement="api_git">
        <dc:Bounds x="1295.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="api_DI_cache" bpmnElement="api_cache">
        <dc:Bounds x="1605.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="api_DI_end" bpmnElement="api_end">
        <dc:Bounds x="1972.0" y="82.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="api_DI_Flow_0" bpmnElement="api_Flow_0">
        <di:waypoint x="168.0" y="100.0" />
        <di:waypoint x="365.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="api_DI_Flow_1" bpmnElement="api_Flow_1">
        <di:waypoint x="515.0" y="100.0" />
        <di:waypoint x="675.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="api_DI_Flow_2" bpmnElement="api_Flow_2">
        <di:waypoint x="825.0" y="100.0" />
        <di:waypoint x="1035.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="api_DI_Flow_3" bpmnElement="api_Flow_3">
        <di:waypoint x="1060.0" y="125.0" />
        <di:waypoint x="1060.0" y="305.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="api_DI_Flow_4" bpmnElement="api_Flow_4">
        <di:waypoint x="1085.0" y="100.0" />
        <di:waypoint x="1295.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="api_DI_Flow_5" bpmnElement="api_Flow_5">
        <di:waypoint x="1135.0" y="340.0" />
        <di:waypoint x="1180.0" y="340.0" />
        <di:waypoint x="1250.0" y="340.0" />
        <di:waypoint x="1250.0" y="30.0" />
        <di:waypoint x="1680.0" y="30.0" />
        <di:waypoint x="1680.0" y="65.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="api_DI_Flow_6" bpmnElement="api_Flow_6">
        <di:waypoint x="1445.0" y="100.0" />
        <di:waypoint x="1605.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="api_DI_Flow_7" bpmnElement="api_Flow_7">
        <di:waypoint x="1755.0" y="100.0" />
        <di:waypoint x="1972.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="DI_git_Participant" bpmnElement="git_Participant" isHorizontal="true">
        <dc:Bounds x="0" y="650" width="2150" height="130" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="view_DI_Pool_view" bpmnElement="view_Pool_view" isHorizontal="true">
        <dc:Bounds x="0.0" y="950.0" width="1220" height="440" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="view_DI_Lane_worker" bpmnElement="view_Lane_worker" isHorizontal="true">
        <dc:Bounds x="30.0" y="950.0" width="1190" height="440" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="view_DI_start" bpmnElement="view_start">
        <dc:Bounds x="132.0" y="1032.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="view_DI_identity" bpmnElement="view_identity">
        <dc:Bounds x="365.0" y="1015.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="view_DI_current" bpmnElement="view_current">
        <dc:Bounds x="725.0" y="1025.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="view_DI_show" bpmnElement="view_show">
        <dc:Bounds x="1042.0" y="1032.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="view_DI_stale" bpmnElement="view_stale">
        <dc:Bounds x="732.0" y="1272.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="view_DI_Flow_0" bpmnElement="view_Flow_0">
        <di:waypoint x="168.0" y="1050.0" />
        <di:waypoint x="365.0" y="1050.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="view_DI_Flow_1" bpmnElement="view_Flow_1">
        <di:waypoint x="515.0" y="1050.0" />
        <di:waypoint x="725.0" y="1050.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="view_DI_Flow_2" bpmnElement="view_Flow_2">
        <di:waypoint x="775.0" y="1050.0" />
        <di:waypoint x="1042.0" y="1050.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="view_DI_Flow_3" bpmnElement="view_Flow_3">
        <di:waypoint x="750.0" y="1075.0" />
        <di:waypoint x="750.0" y="1272.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="DI_Message_repository_watch_evidence_0" bpmnElement="Message_repository_watch_evidence_0">
        <di:waypoint x="1370" y="135" />
        <di:waypoint x="1370" y="550" />
        <di:waypoint x="1100" y="550" />
        <di:waypoint x="1100" y="650" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="1150" y="510" width="260" height="42" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="DI_Message_repository_watch_evidence_1" bpmnElement="Message_repository_watch_evidence_1">
        <di:waypoint x="1680" y="135" />
        <di:waypoint x="2080" y="135" />
        <di:waypoint x="2080" y="870" />
        <di:waypoint x="440" y="870" />
        <di:waypoint x="440" y="1015" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="1250" y="830" width="260" height="42" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

Sysadmin собирает локальные source facts, отдельно определяет configured deployment target и canonical upstream target. Для public-целей выполняет anonymous ls-remote и при необходимости сравнение истории во временном repository. В рабочем checkout проверка не делает merge/reset/pull. Независимые repositories обрабатываются небольшим bounded worker pool, до четырёх workers, а не одной последовательной цепочкой всех сетевых тайм-аутов.

Состояния различают CURRENT, UPDATE_AVAILABLE, LOCAL_AHEAD, DIVERGED, SOURCE_UNAVAILABLE, PRIVATE_AUTH_REQUIRED и UNKNOWN. Dirty worktree не должен выдавать ложный CURRENT. Совпадение deployment и upstream цели позволяет переиспользовать одно evidence; иначе цели проверяются независимо. Дополнительно хранится предусмотренная публикационная проекция source evidence, не криптографическое подтверждение всей удалённой установки.

Private-цель без разовых credentials возвращает PRIVATE_AUTH_REQUIRED. Фоновая operation не читает private credentials и не пытается автоматически использовать пользовательское хранилище. Public Git-вызовы отключают интерактивный prompt/askpass и credential helper. Отдельная ручная private-refresh операция существует в Sysadmin, но не вызывается этим handler, её credentials не сохраняются в snapshot. Копировать секрет в Blockly/Wiki/описание operation не требуется.

Результаты сохраняются как нечувствительное invalidatable evidence в managed conf-real/repository-update-status.json и возвращается summary. Ошибка/недоступность конкретной цели может быть штатным результатом evidence, а не доказательством поломки всего handler.

Обычный read path страницы статусов не делает Git network I/O. Он сопоставляет сохранённую identity snapshot с текущими source/branch/commit/worktree facts; несоответствующее evidence не должно выдавать прежний CURRENT как актуальную проверку. Открытие страницы и запуск refresh — разные операции.

Диагностика и эксплуатационный смысл

Проверяйте отдельно enabled/runtime version, identity/capability, момент последней успешной operation и состояние каждой deployment/upstream цели. Не считайте status=ok доказательством, что все repositories доступны или не имеют обновлений. Не считайте PRIVATE_AUTH_REQUIRED общей сетевой недоступностью сервера.

После изменения ветки, source URL, commit или рабочего дерева старый snapshot может стать неактуальным — это ожидаемая защита от ложного CURRENT. Решение применить обновление принимает оператор через штатный repository/deployment workflow; этот монитор не является его заменой.

Проверенные источники

Git SHA/SHA-256 и модели закреплены в metadata/BPMN; перед упаковкой они повторно сверены. Сама operation, локальные installation facts и её сетевые Git-проверки при анализе не запускались. Для анализа прочитан только опубликованный source Git. Общий автоматический hook обновлений Wiki пока не завершён.

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

sharixopen/knowledge/scenarios/repository_update_watch.1793224814.txt.gz · Последнее изменение: — sx_knowledge_api_open

© 2022 ShariX