FTS pipeline и визуализация: один прогон и диаграмма модели
Курс уже показал fts check, fts prove, fts certify и fts visualize
как отдельные команды — у каждой свой JSON, свой exit code, своя причина
упасть. В CI и в диалоге с агентом это неудобно: чтобы понять, готов ли
документ, надо запустить четыре процесса и вручную сопоставить их вывод.
Эта глава — про fts pipeline, который прогоняет всю цепочку одним вызовом
и возвращает один JSON, и про fts visualize, который превращает модель в
mermaid-диаграмму, — то есть в вопрос, который можно задать человеку, а не
только компилятору.
Минимальный пример
категория «Исполнение заказа»
объект Заказ
номер является строкой
клиент является строкой
оплачен является признаком
«склад подтвердил» является признаком
«готов к отгрузке» является состоянием «Готов к отгрузке»
морфизм «Готовый заказ можно отгрузить»
если «Готов к отгрузке»
то «Отгрузить заказ разрешено»
теорема «Заказ ЗК-7781 можно отгрузить»
дано Заказ имеет «готов к отгрузке» равное да
в данных заказы найти где номер равен «ЗК-7781»
по морфизму «Готовый заказ можно отгрузить»
следовательно «Отгрузить заказ разрешено»
Это static/fts/models/order-shipment.fts — та же модель, что в главе про
доказательства и сертификаты. Здесь она нужна по другой причине: в ней есть
и структура, и морфизм, и теорема, а значит на ней можно показать все четыре
поля PipelineResult сразу, а не только часть.
Что делает компилятор
fts check разбирает категорию и объект, проверяет, что состояние
«Готов к отгрузке» объявлено, а морфизм ссылается на реально существующий
факт. Если бы теорема ссылалась на несуществующее поле, компиляция упала бы
здесь же, до всякого прогона данных.
Один прогон вместо четырёх команд
pipeline() в src/pipeline.ts — это не пятая команда, а последовательный
вызов того же кода, что стоит за check, prove, certify и visualize,
на одном уже скомпилированном документе:
export function pipeline(input: PipelineInput): PipelineResult {
const rawDocument = typeof input.source === "string"
? compile(input.source)
: normalizeDocument(input.document)
const document = assertValid(rawDocument)
const proof = document.proposition === null ? null : prove(document, input.context)
const certificate = document.proposition === null ? null : certify(document, input.context)
const viz = visualize(document, proof, input.viz ?? "all")
return { document, proof, certificate, viz }
}
PipelineInput принимает либо строку source, либо уже разобранный
document (для тех, кто получил канонический JSON из другого языка, — см.
главу про cross-language), плюс опциональные context и viz.
PipelineResult — это ровно { document, proof, certificate, viz }: если в
модели нет теоремы (document.proposition === null), proof и
certificate будут null, но document и viz всё равно вернутся —
диаграмму можно построить и без теоремы.
Важно, чего в pipeline нет: он не запускает fts test и не вызывает
generateTypeScript. Тесты примеров и генерация кода — отдельные шаги CI,
намеренно не входящие в эту цепочку (одна из типичных ошибок ниже).
Практическая разница с четырьмя отдельными вызовами — не в количестве нажатий клавиш, а в форме отказа:
- Одна точка отказа.
assertValidбросает исключение с той же диагностикой, что иfts check, и весьpipelineостанавливается на этом шаге — не нужно писать в CIcheck && prove && certify && visualizeс ручной проверкой exit code на каждом шаге. - Один JSON для CI. Результат можно целиком сохранить как build-артефакт или один раз распарсить в скрипте, вместо четырёх файлов, которые могут разойтись по времени, если процесс упал между шагами.
- Один JSON для агента. MCP-инструмент
fts_pipeline(src/mcp.ts) описан так: «Compile, validate, prove, and visualize FTS in one deterministic call». Агенту не нужно делать четыре последовательных вызова инструмента и держать в контексте четыре промежуточных результата — он получает документ, доказательство, сертификат и диаграмму одним round-trip и с одним и тем жеdocument_digestво всех частях ответа.
CLI-эквивалент:
fts pipeline order-shipment.fts --context order-shipment.context.json --mode category --pretty
Диаграмма категории
fts visualize строит mermaid из документа, а не рисует его вручную —
диаграмма настолько же точна, насколько точна модель. Вот реальный вывод
mermaidCategory для order-shipment.fts:
номер: Строка<br/>клиент: Строка<br/>оплачен: Признак<br/>склад подтвердил: Признак<br/>готов к отгрузке: Готов к отгрузке"] n_u413_u43e_u442_u43e_u432_u20_u43a_u20_u43e_u442_u433_u440_u443_u437_u43a_u435_["Готов к отгрузке"] n_u41e_u442_u433_u440_u443_u437_u438_u442_u44c_u20_u437_u430_u43a_u430_u437_u20_u440_u430_u437_u440_u435_u448_u435_u43d_u43e_["Отгрузить заказ разрешено"] n_u413_u43e_u442_u43e_u432_u20_u43a_u20_u43e_u442_u433_u440_u443_u437_u43a_u435_ -->|"morphism Готовый заказ можно отгрузить"| n_u41e_u442_u433_u440_u443_u437_u438_u442_u44c_u20_u437_u430_u43a_u430_u437_u20_u440_u430_u437_u440_u435_u448_u435_u43d_u43e_ end
Вопрос, который снимает эта картинка: «сколько вообще типов в модели и какие
переходы между ними объявлены» — без чтения .fts построчно. Узел структуры
показывает все поля сразу, стрелка — направление морфизма от домена к
кодомену. mermaidCategory собирает узлы по именам структур и функторов
(document.structures, document.functors), поэтому диаграмма никогда не
разойдётся с моделью: изменили поле — изменилась подпись узла при следующей
генерации.
Диаграмма морфизмов и доказательства
Категорию с одним морфизмом читать легко и без диаграммы. Диаграмма нужна,
когда морфизмов несколько и важен порядок композиции. На модели
credit-limit.fts (кредитный лимит: скоринг → риск-проверка → лимит)
mermaidFunctors рисует цепочку явно:
Вопрос, который снимает эта диаграмма: «в каком порядке на самом деле
применяются морфизмы» — в JSON-выводе prove порядок композиции печатается
слева направо в порядке объявления, а математическое ∘ читается справа
налево (это отдельно разобрано в главе про доказательства). Картинка с
двумя явными стрелками устраняет двусмысленность быстрее, чем разбор
строки A ∘ B.
mermaidProof строит третью диаграмму — не из модели, а из результата
prove на конкретных данных: цепочку применённых морфизмов и witness,
которым закрывается вывод:
Вопрос здесь другой: «на основании какого именно факта система разрешила
лимит для конкретной заявки». Пунктирная стрелка к узлу witness — это и
есть ответ: конкретное поле конкретного объекта, а не абстрактный закон.
Кому и когда показывать диаграмму
- Аналитику — диаграмму категории, до code review. Она проверяется
глазами за минуту: совпадают ли имена узлов с ubiquitous language из
задачи, не потерялся ли морфизм, который аналитик считал очевидным.
Текст
.ftsдля этого читать не обязательно. - Новому разработчику в команде — диаграмму категории и морфизмов при первом знакомстве с моделью. Один граф заменяет чтение файла целиком и сразу показывает границы: какие структуры существуют, какие переходы между ними объявлены, а какие похожие переходы в модели попросту отсутствуют.
- При разборе инцидента — диаграмму доказательства (
mermaidProof) на том снимке данных, что был в момент инцидента. Это единственная из трёх диаграмм, которая отвечает не «что вообще возможно», а «что именно произошло на этих данных»: конкретный путь и конкретный witness, а не общая схема модели.
Практика в песочнице
Вкладка «Диаграмма» вызывает mermaidCategory прямо в браузере — это тот же
код, что и в fts visualize, но без установки CLI. Откройте эту модель и
сравните диаграмму категории с диаграммой морфизмов из этой главы: два узла
«Риск-проверка разрешена» в mermaidFunctors — это один и тот же узел
модели, просто нарисованный дважды, для каждого морфизма отдельно.
Типичные ошибки
- Ждать от
pipelineтестов и генерации. Название звучит как «весь жизненный цикл модели», ноpipeline()— этоcheck+prove+certify+visualize, и ничего больше.fts testиfts generateостаются отдельными шагами CI (см. главу про генерацию и CI); включать их вpipelineбессмысленно ждать. - Перепутать позиции аргументов
visualize. Сигнатура —visualize(document, proof, mode), три параметра, а не два. Вызов видаvisualize(doc, "all")подставит строку"all"вместоproofи упадёт сTypeError: Cannot read properties of undefined (reading 'length')внутриmermaidProof— ровно эта ошибка воспроизводится, если проверить на вендорной сборке сайта. Третий аргумент обязателен, второй — либоnull, либо результатprove. - Забыть про
nullвproof/certificate. Для модели без теоремы (document.proposition === null)pipelineне падает — он просто возвращаетproof: null, certificate: nullпри полностью заполненномviz. Код, который читаетresult.proof.witnessбез проверки наnull, упадёт не на модели с ошибкой, а на корректной модели без теоремы. - Считать
functorsсинонимом «нет диаграммы». У--modeпять реальных значений —all,category,morphisms,functors,proof(morphismsиfunctorsсинонимичны в реализацииvisualize, оба ведут кmermaidFunctors); если в модели нет ни одного морфизма,mermaidFunctorsвернёт пустую строку, ноvisualizeв режимахmorphisms/functorsоткатится наmermaid_category, а не на пустой вывод.
Чек-лист
- Для CI и агента используется
fts pipeline/fts_pipeline, а не последовательность изcheck,prove,certify,visualize. - Известно, что
pipelineне подменяетfts testиfts generate— они остаются в конвейере отдельно. - Перед чтением
result.proofилиresult.certificateпроверено, что документ содержиттеорема(иначе оба поля —null). - Диаграмма категории показана аналитику до code review, а не после.
- Диаграмма доказательства строится на снимке данных инцидента, а не на абстрактной модели, если вопрос — «что произошло», а не «что возможно».