Composition Rewrite ADR

Table of Contents

1. Статус и назначение документа

Статус: принят и описывает текущий patch-oriented workflow.

Назначение: зафиксировать, почему переписывание композиции оформлено как preview/apply процесс с patch-моделью по блокам, а не как прямое переписывание композиции целиком.

2. Контекст

В TextFoundry композиция состоит из структуры фрагментов, а ключевой переиспользуемой единицей смысла остаётся блок. Пользователь нередко хочет:

  • доработать композицию по новой инструкции;

  • улучшить тексты блоков;

  • сохранить общую структуру и состав композиции;

  • не пересобирать композицию вручную целиком.

При этом система должна удерживать важные ограничения:

  • не менять состав блоков без явного authoring;

  • не ломать placeholders;

  • не превращать rewrite в скрытый redesign композиции;

  • давать пользователю inspectable preview до публикации.

3. Проблема, которую нужно было решить

Нужно было добавить AI-assisted rewrite существующей композиции так, чтобы он:

  • обновлял содержание блоков, а не структуру композиции;

  • был совместим с block versioning;

  • позволял человеку просмотреть proposed changes;

  • не превращался в непрозрачный one-shot overwrite.

Прямая перезапись композиции здесь опасна, потому что теряется понимание:

  • какие именно блоки изменились;

  • какие поля были затронуты;

  • не сломаны ли placeholders;

  • опубликована ли новая версия осознанно.

4. Рассмотренные варианты

Вариант A. Переписать композицию целиком как готовый текст

Идея: отдать модели весь render или композицию и получить новый финальный текст.

Плюсы:

  • проще с точки зрения одной операции;

  • пользователю легко объяснить "переписать всё".

Минусы:

  • теряется связь с block structure;

  • блоки перестают быть главной единицей сопровождения;

  • невозможно аккуратно выпустить новые версии только нужных блоков;

  • высок риск unintended redesign.

Вердикт:

  • отклонено.

Вариант B. Разрешить модели напрямую мутировать композицию без preview

Идея: LLM предлагает изменения, а система сразу применяет их к хранилищу.

Плюсы:

  • минимум шагов в UX;

  • быстрая автоматизация.

Минусы:

  • нет inspectable review stage;

  • сложнее локализовать ошибку;

  • выше риск повредить production-ценные блоки;

  • пользователь фактически теряет контроль над публикацией.

Вердикт:

  • отклонено.

Вариант C. Patch-oriented preview и отдельный apply

Идея: модель возвращает не новую композицию целиком, а набор BlockRewritePatch, которые пользователь сначала просматривает, а потом отдельно применяет.

Плюсы:

  • видно, какие блоки реально затронуты;

  • проще удерживать структуру композиции;

  • engine может валидировать placeholders и дубликаты patch entries;

  • apply можно трактовать как обычный controlled versioning workflow.

Минусы:

  • больше шагов;

  • preview UI сложнее простого textarea;

  • модель должна быть дисциплинирована и возвращать частичный patch, а не новый design всей композиции.

Вердикт:

  • выбранный вариант.

5. Принятое решение

Принято решение оформлять composition rewrite как двухфазный процесс:

  • PreviewCompositionBlockRewrite(…​)

  • ApplyCompositionBlockRewrite(…​)

Ключевые правила:

  • переписываются только блоки, уже присутствующие в композиции;

  • composition structure, block ids и порядок блоков не меняются;

  • preview обязателен перед apply;

  • apply публикует новые версии затронутых блоков и новую версию композиции;

  • placeholders должны сохраняться.

6. Детализация принятого решения

6.1. Rewrite работает на уровне блоков, а не на уровне итогового текста

Причина: в TextFoundry composition - это структура из block refs и других фрагментов, а не просто строка.

Поэтому в контекст rewrite передаются:

  • block_id;

  • type;

  • language;

  • description;

  • defaults;

  • tags;

  • templ.

Такое решение удерживает rewrite внутри модели "улучшить существующие блоки", а не "сгенерировать новую композицию как попало".

6.2. Preview является обязательным отдельным артефактом

Причина: между предложением модели и записью в storage должен существовать inspectable буфер принятия решения.

CompositionBlockRewritePreview хранит:

  • исходную композицию и версию;

  • список patch entries;

  • рациональ каждого patch.

Это даёт:

  • понятную review stage;

  • возможность показать before/after summary;

  • отделение AI suggestion от фактической mutation.

6.3. Patch-модель сознательно ограничена частичными изменениями

Причина: чем богаче patch format, тем выше риск дать модели возможность менять слишком много.

Поэтому patch разрешает изменять только:

  • description;

  • templ;

  • defaults;

  • tags;

  • rationale.

Он не даёт:

  • добавлять новые блоки;

  • удалять блоки;

  • менять block_id;

  • менять type;

  • менять структуру композиции.

Это главный safety boundary всей функции.

6.4. Apply публикует новые версии блоков, а затем новую версию композиции

Причина: rewrite не должен обходить обычный lifecycle сущностей.

При применении engine:

  • валидирует patch set;

  • загружает исходные блоки из композиции;

  • собирает обновлённые block drafts;

  • проверяет placeholders;

  • вызывает UpdateBlock(…​);

  • затем публикует новую версию композиции с обновлёнными ссылками.

Это позволяет сохранить версионирование и воспроизводимость истории.

6.5. Placeholder preservation контролируется engine, а не только prompt policy

Причина: если положиться только на текстовую инструкцию модели, rewrite может случайно сломать runtime contract блока.

Поэтому перед UpdateBlock(…​) engine сравнивает множества placeholders исходного и переписанного шаблонов. Любое нарушение приводит к ошибке.

Это фиксирует правило: rewrite допустим только пока он не ломает executable semantics шаблона.

6.6. Повторные ссылки на один и тот же блок не должны порождать лишние patch entries

Причина: композиция может ссылаться на один и тот же блок более одного раза.

В preview context engine собирает уникальные block ids и не передаёт один и тот же блок модели несколько раз. Это уменьшает шум и риск конфликтов.

7. Последствия решения

Положительные:

  • mutation остаётся inspectable;

  • блоки сохраняют роль главной единицы сопровождения;

  • структура композиции защищена от случайного redesign;

  • apply интегрируется в существующий versioning workflow.

Компромиссы:

  • пользователю нужно сначала делать preview, а потом apply;

  • preview показывает summary patches, а не полный rich diff;

  • часть desired changes пользователь всё равно может предпочесть сделать вручную в редакторе.

Отрицательные:

  • некоторые сценарии "перепридумать композицию" intentionally не покрываются;

  • если instruction слишком общая, preview может быть малополезным;

  • apply затрагивает несколько сущностей и требует более аккуратной обработки ошибок.

8. Риски и меры снижения

Риск: модель попробует изменить структуру композиции через содержательные rewrite

Снижение:

  • patch format не поддерживает structural changes;

  • prompt constraints требуют сохранять composition structure.

Риск: preview будет трудно интерпретировать

Снижение:

  • UI строит before/after summary;

  • у patch есть rationale;

  • изменения показываются по блокам, а не общей массой текста.

Риск: patch повредит placeholders

Снижение:

  • engine-level placeholder validation до публикации.

Риск: один и тот же блок будет переписан неоднозначно

Снижение:

  • уникализация block ids в preview context;

  • запрет duplicate patches по block_id.

9. Что сознательно не покрывается

Данный ADR не фиксирует:

  • structural rewrite композиции;

  • массовое добавление и удаление блоков через AI;

  • автоматический merge разных preview между собой;

  • полноценный визуальный diff на уровне редактора композиции.

10. Связанные документы