Материализация файлов
Нода materialize доставляет файлы, заданные workflow, в файловую систему агента, удерживая их
отрендеренные тела вне ответа шага. Moira выдаёт команду загрузки короткоживущего архива,
приостанавливает выполнение и продолжает его только после пустого ответа агента. Хост, который не
может выполнить эту команду, доставляет файлы описанным ниже fallback-маршрутом в контекст и
завершает шаг так же.
Используйте эту ноду для стабильных файлов, которыми владеет определение workflow: инструкций,
стандартов или пустого каркаса директорий. Файлы, чьё содержимое зависит от анализа агента, остаются
ответственностью создающей их agent-directive ноды.
Объявление ноды
Храните переиспользуемый текст в строковой записи variableRegistry и ссылайтесь на неё через
from:
{ "variableRegistry": { "workspace_reference": { "type": "string", "description": "Инструкции, доставляемые в рабочее пространство", "default": "# Workspace reference\n\nFollow the project contract." } }, "nodes": [ { "id": "materialize-workspace", "type": "materialize", "basePath": "{{workspace_path}}", "files": [ { "path": "reference.md", "from": "workspace_reference" }, { "path": "plans/.keep", "content": "" } ], "connections": { "success": "work", "error": "materialize-failed" } } ]}| Свойство | Обязательно | Контракт |
|---|---|---|
basePath | Да | Шаблонизируемая директория назначения; отрендеренное значение должно быть непустым и не содержать NUL |
files | Да | От 1 до 100 записей архива |
files[].path | Да | Безопасный шаблонизируемый путь относительно basePath |
files[].from | Одно из | Имя строковой записи реестра, чей текущий default задаёт тело файла |
files[].content | Одно из | Должен быть ровно ""; создаёт пустой каркасный файл |
connections.success | Да | Следующая нода после пустого ответа |
connections.error | Нет | Маршрут ошибки валидации, конфигурации, базы данных или выдачи разрешения во время показа шага |
Каждый файл должен объявлять ровно одно из полей from и content. Непустой inline-content
отклоняется; вместо него используйте одну запись реестра как источник истины.
Запуск сгенерированной команды
При показе ноды Moira рендерит basePath и сводку путей, создаёт разрешение на пять минут и
возвращает POSIX-команду, где каждый аргумент уже безопасно заключён в shell-кавычки:
mkdir -p -- '<basePath>' && curl -sSf -- '<reusable-url>' | tar -x -C '<basePath>'Запускайте выданную команду без изменений. URL — непрозрачный bearer credential: не собирайте его
самостоятельно, не редактируйте, не записывайте в лог и не передавайте другим. Одну и ту же команду
можно повторять в течение пяти минут, пока выполнение ожидает на этой ноде. После успешной распаковки
завершите шаг значением null или {}. Иная форма ответа не принимается.
Сгенерированная директива сообщает срок и правила повтора, предупреждает, что переход выполнения
делает URL недействительным, называет описанный ниже fallback-маршрут доставки в контекст при
условии, что этот хост не может выполнить команду, и объясняет, что доставка не доказывает чтение
ни на одном из маршрутов. Вызов
session({ action: "current_step" }) во время паузы выдаёт новые команду и разрешение, не продвигая
граф. Поздняя директива всё равно должна явно требовать чтения каждого используемого файла.
Контракт архива и путей
При запросе URL Moira заново загружает текущее определение workflow. Она рендерит каждый путь и каждое тело из реестра с контекстом выполнения, привязанным к разрешению, а затем создаёт несжатый tar-архив. Поэтому изменение workflow после выдачи команды может поменять пути и содержимое архива, но не директорию назначения, уже встроенную в команду.
Записи архива содержат только пути относительно basePath; сама директория назначения в архив не
входит. Объявленные и отрендеренные пути должны быть нормализованными, непустыми, относительными и
уникальными. Moira отклоняет NUL, абсолютные и начинающиеся с обратной косой черты пути, пустые
сегменты, а также сегменты . и ...
Ограничения применяются к отрендеренному UTF-8-содержимому:
- не более 100 файлов;
- не более 1 MiB на файл;
- не более 10 MiB несжатого содержимого суммарно.
Файлы создаются с режимом 0644. Движок проверяет basePath, но не ограничивает назначение
директорией проекта. Авторы workflow должны получать его из доверенного пути рабочего пространства,
а агенты — проверять назначение в выданной команде перед запуском.
Разрешение и обработка ошибок
Пятиминутное разрешение хранится на сервере и привязано к текущим пользователю, выполнению и ноде. Выполнение должно оставаться в состоянии running и ожидать на той же materialize-ноде. Сервер заново проверяет эти привязки при каждом запросе, поэтому одно разрешение поддерживает повторные загрузки в течение абсолютного пятиминутного окна, но перестаёт работать сразу после перехода выполнения. Логи запросов скрывают credential из materialize-URL.
| Ошибка | Результат |
|---|---|
| Некорректная нода или директория назначения, ошибка конфигурации, БД или выдачи разрешения при показе шага | Переход по connections.error, если он задан; иначе ошибка выходит из выполнения |
| Некорректный, истёкший или неверно привязанный URL | HTTP 401 с Invalid or expired materialize token |
| Некорректный отрендеренный путь, отсутствующий источник в реестре, ошибка шаблона или превышение лимита | HTTP 400 с Materialize archive could not be generated |
Локальная ошибка curl, pipe, файловой системы или tar | Недоступная сеть подпадает под fallback-доставку в контекст; ошибка файловой системы или tar остаётся блокером, о котором агент сообщает, не завершая шаг |
| Доставка в контекст набора больше 256 KiB | Инструмент отвечает именованным отказом и не доставляет файлы |
connections.error не перехватывает ошибку загрузки или распаковки: эти операции выполняются уже
после показа шага.
Доставка в контекст агента
У хоста, который в текущем ходе не может выполнить shell-команду или обратиться к сети, есть второй маршрут:
session({ action: "materialize", executionId: "<process-id>" })Он возвращает те же отрендеренные тела, что содержал бы архив: по одному текстовому блоку на файл, с заголовком в виде пути этого файла, и ничего не пишет в файловую систему. Разрешение при этом не передаётся: сервер сам находит разрешение текущего показа ноды по выполнению вызывающего, поэтому URL архива не обязан проходить через ответ.
Этот маршрут расходует контекст, поэтому он именно fallback, а не основной путь, и предъявляемая директива прямо это говорит. Когда хост способен выполнить выданную команду, используйте её.
Разрешение здесь то же, что и у архивного канала: тот же пользователь, то же выполнение, всё ещё ожидающее на той же ноде, та же ревизия контекста, то же пятиминутное окно. Любой отказ отвечает одним и тем же сообщением, не называя несработавшее условие, поэтому отказ не раскрывает, принадлежит ли выполнение другому пользователю.
Поскольку тела попадают в контекстное окно, а не на диск, этот маршрут дополнительно отклоняет набор суммарно больше 256 KiB — заметно ниже лимитов архива выше. Он именно отклоняет, а не усекает: укороченный файл неотличим для читающего его агента от полного, а для набора такого размера остаётся выданная команда.
Доставка по-прежнему не доказывает чтение. Прочитайте каждый доставленный файл, который требуется
последующей директиве, и завершите шаг значением null или {} как обычно.
Применение в Workflow Management Flow
Workflow Management Flow один раз определяет рабочее пространство, затем материализует стабильные bootstrap-файлы и только после этого выбирает ветку создания или редактирования:
get-action-type -> materialize-workspace-bootstrap -> route-action-type | create -> gather-workflow-requirements | edit -> prepare-edit-workflowЕго materialize-объявление эквивалентно следующему:
{ "id": "materialize-workspace-bootstrap", "type": "materialize", "basePath": "{{workspace_path}}", "files": [ { "path": "process-id.txt", "from": "workspace_process_id_file" }, { "path": "workflow-authoring-reference.md", "from": "workflow_authoring_reference" } ], "connections": { "success": "route-action-type" }}Предыдущий владелец записывает глобальный workspace_path. Значения реестра предоставляют ID
выполнения и справочник автора. Последующие владельцы веток create и edit записывают динамические
требования, provenance, планы и отчёты ревью, но не перезаписывают стабильные bootstrap-файлы.
Перевод ручного bootstrap
Чтобы заменить директиву агента, вручную записывающую стабильные файлы workflow:
- Перенесите каждое стабильное тело в
defaultотдельной строковой записиvariableRegistry. - Поручите существующей ранней ответственности вернуть доверенный глобальный путь для
basePath. - Вставьте одну ноду
materializeпосле этого владельца и перед первым потребителем файлов. - Удалите из последующих директив только инструкции записи соответствующих статических файлов. Динамические файлы оставьте ответственности, определяющей их содержимое.
- Провалидируйте workflow и проверьте выданную команду, записи архива, отрендеренные тела, а также успешный и ошибочный маршруты.