Agentic Engineering PlatformFrozen Template ConstructorNo Code GenerationContent ArchitectureMCP

Как работает Конструктор замороженных шаблонов: путь от «сделай мне новости» до готового раздела

Подробный разбор простыми словами: что происходит от момента запроса пользователя до появления готового раздела на сайте — кто принимает запрос, какие инструменты вызываются, какие действия выполняются по шагам, как доставляется движок и дизайн-макет, насколько он гибкий и как его адаптировать под другие структуры. С реальными именами компонентов, функций, навыков и MCP.

Roma Armstrong20 мин чтения
Раньше добавить раздел значило, что ИИ-агент вручную пишет тридцать файлов. Конструктор делает это за один вызов — собирая раздел из проверенных деталей, не генерируя ни строки кода. Эта статья — про то, как именно.

Это подробный разбор для тех, кто хочет понять механизм до конца, а не на уровне «оно как-то само». Мы пройдём весь путь: от фразы пользователя «сделай мне раздел новостей» до готовых страниц на сайте — кто принимает запрос, какие инструменты вызываются, что они делают по шагам, как доставляется и из чего собран дизайн-макет, и как всё это адаптировать под другие задачи. По дороге будут настоящие имена файлов, функций, навыков и MCP — чтобы по этому документу можно было задавать точные вопросы и развивать проект дальше. Платформа, о которой речь, — платформа для агентной инженерии.

Кто участвует — карта компонентов

Сначала познакомимся с действующими лицами. В сборке участвует несколько частей, и у каждой — своя роль. Важная мысль наперёд: ни одна часть не генерирует код. Всё, что происходит, — это копирование проверенных файлов и подстановка значений (названия раздела, языков, метки). Поэтому результат одинаков у любой ИИ-модели.

КомпонентЧто это и зачемГде живёт (реальное имя)
Гермес (или любой агент)Мозг/оркестратор: принимает запрос, понимает намерение, выбирает инструмент, переспрашивает перед изменением. Может быть и одиночный кодящий агент — Гермес не обязателен.процесс fractera-hermes; персона SOUL.md
Навык compose-frozen-templateИнструкция-сценарий «как собрать структуру»: по каким осям сопоставлять запрос, что подтвердить, что вызвать. Лежит во ВСЕХ шести агентах (самодостаточность)..agents/skills/compose-frozen-template/SKILL.md
MCP-мост template-constructor-bridgeКанал, через который агент вызывает сборку как инструмент. Порт 3224. Два инструмента (ниже).bridges/platforms/template-constructor-mcp-server.js
Data-сервис + закрытый сторХранит «базис» — проверенные детали (кирпичи) и движок. Отдаёт их только для чтения. Порт 3300.services/data/server.js · стор services/data/frozen-templates/
Композер (эмиттер)Скрипт, который реально собирает раздел в проекте: копия файлов + подстановка токенов. Сердце механизма..agents/skills/compose-frozen-template/compose-frozen-template.mjs
Движок отрисовки (engine)Общий «дизайн-макет» и логика, как страница выглядит и формирует SEO. Доставляется в проект один раз.стор → engine/ → ставится в слот как lib/content-v1/ и components/content-page-v1/**
Инструмент пересборкиДелает изменения видимыми: та же кнопка «Deploy», что в подвале. Порт 3225.bridges/platforms/deploy-mcp-server.js · owner_deploy_rebuild_slot

Путь запроса — шаг за шагом

Теперь пройдём весь маршрут. Каждый шаг — это конкретное действие конкретного компонента.

Шаг 1. Запрос пользователя

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

Шаг 2. Гермес понимает запрос и переспрашивает

Агент загружает навык compose-frozen-template и действует строго по нему. Перед любой генерацией он читает набор языков сайта из переменной NEXT_PUBLIC_SUPPORTED_LANGUAGES (это прививка против бага «сайт на языке, которого у него нет»). Затем, поскольку сборка изменяет проект, действует правило «переспроси перед изменением»: агент проговаривает обратно — «Если я правильно понимаю: собрать раздел формата news по адресу /news, языки en, ru, две страницы-заглушки. Делаю?» — и ждёт явного «да».

Шаг 3. Матчинг по «оболочке» (envelope)

Это ключевая идея конструктора. Каждая структура описывается несколькими независимыми осями. Запрос «проецируется» на эти оси, и подбирается примитив (кирпич), который подходит на 100% по каждой оси. Не подходит хотя бы по одной — это уже не тот кирпич, и будет честный отказ с указанием оси. Чтобы увидеть доступные кирпичи, агент вызывает инструмент owner_template_list_primitives (только чтение).

ОсьЧто значитЗначения сегодняСлот
source (источник)Откуда берётся список «детей» разделаfiles (скан файлов) · db-at-build (запрос к БД на сборке) · runtimeA — поставщик списка
depth (глубина)Сколько уровней вложенности (хаб → подхаб → …)1 (плоский список) · 2 · 3 · 4базовая
rendering (отрисовка)Статика или «динамический дескриптор», когда статика невозможнаstatic · dynamic-descriptorбазовая
i18n (языки)Многоязычность, одинаково на каждом уровнеmono · multiB — единообразный аспект
roles (доступ)Кому виден раздел; гейт встраивается одинаково на каждый уровеньpublic · guest · список ролейB — единообразный аспект

Сегодня в базисе один готовый кирпич — `files-depth1`: плоский список документов из файлов, многоязычный (новости / блог / лента документации). Остальные клетки сетки — в дорожной карте, их «заморозят», только когда они докажут себя живой разработкой.

Шаг 4. Предпросмотр (dry_run)

Перед реальной записью агент вызывает инструмент сборки с флагом `dry_run: true`. Это возвращает предпросмотр — что именно будет создано (адрес раздела, число страниц, языки, метка, формат), без единой записи на диск. Агент показывает это пользователю и получает подтверждение.

Шаг 5. Вызов инструмента сборки

После «да» агент вызывает мутирующий инструмент owner_template_compose_structure (MCP-мост на порту 3224). Этот инструмент сам ничего не «придумывает» — он оркеструет:

  1. тянет «базис» из закрытого стора: GET http://127.0.0.1:3300/frozen-templates/tree — весь набор файлов как карта «путь → содержимое»;
  2. распаковывает этот набор во временную папку на сервере;
  3. запускает композер: spawn node compose-frozen-template.mjs --store <временная папка> --out <папка слота> --tab news --format news --languages en,ru --label-en News --label-ru Новости --samples 2 …;
  4. разбирает вывод композера (успех или честный отказ по оси) и считает публичные адреса страниц (view_urls), учитывая режим: secure → https://домен/язык/раздел, IP-режим → http://ip:3000/...;
  5. возвращает результат и напоминание: теперь нужна пересборка слота.

Шаг 6. Что делает композер (эмиттер) — по порядку

Скрипт compose-frozen-template.mjs — детерминированный сборщик. Он выполняет строго такую последовательность (имена функций — настоящие):

  1. МАТЧ. Находит в registry.json примитив, у которого базовые оси (source, depth, rendering) РАВНЫ запросу; если нет — refuse() с указанием оси (выход 2). Проверяет, что нужные аспекты (i18n, roles) этот кирпич поддерживает.
  2. УСТАНОВКА ДВИЖКА — installEngine(). Копирует движок в слот «если файла ещё нет» (copy-if-absent). Версионируемую часть кладёт в неймспейс lib/content-v1/ и components/content-page-v1/ (versionizeContent / versionizePath); личность (brand, author, languages) НЕ версионирует — она одна на сайт.
  3. СБОРКА ВКЛАДКИ — composeTab(). Копирует шаблон вкладки из primitives/files-depth1/tab/** в app/[lang]/<раздел>/, подставляя токены ({{TAB}}, {{TAB_PASCAL}}, {{FORMAT}}, {{LABEL}} и т.д.).
  4. ШОВ A (источник списка). Копирует providers/files/list-provider.ts в _lib вкладки — это «откуда берётся список». Сменить files на БД = поменять только этот шов.
  5. ШОВ B (аспекты). Если включены роли — оборачивает содержимое макета в <RouteGuard …> через токены {{ASPECT_OPEN}}/{{ASPECT_CLOSE}} в layout.tsx, одинаково на каждом уровне.
  6. ДАННЫЕ И ЯЗЫКИ. Пишет _data/en.ts (полная база + метка), на каждый дополнительный язык — частичный override _data/<lang>.ts (перевод только метки), и _data/index.ts со сборкой языков (per-key fallback на английский).
  7. СТРАНИЦЫ-ЗАГЛУШКИ. Из шаблона __ITEM__ печёт N документов (sample-1, sample-2 …): Lorem-текст + картинка-заглушка, но с идеальной структурой (SEO-метаданные, хлебные крошки, оглавление, FAQ).
  8. СПИСОК И РЕГИСТРАЦИЯ. Пишет _list.generated.ts напрямую; registerCollection() добавляет вкладку в lib/parser-fs.mjs (COLLECTIONS); patchPackageJson() прописывает скрипты gen:lists / predev / prebuild; appendSitemap() напоминает добавить путь в sitemap.

Шаг 7. Пересборка — иначе изменения не видны

Слот работает в продакшен-режиме: только что записанные файлы НЕ видны, пока проект не пересобран. Поэтому финальный шаг — вызвать инструмент owner_deploy_rebuild_slot (MCP-мост деплоя, порт 3225). Это ровно та же «Deploy», что и кнопка в подвале сайта: пересборка (next build) + перезапуск процесса (pm2 reload) + проверка здоровья. Никаких секретов в чат — это делает сам инструмент или кнопка владельца.

Шаг 8. Результат

После успешной пересборки агент сообщает реальные публичные адреса из view_urls / site_url (например, «Готово — раздел новостей доступен по https://ваш-домен/ru/news»). Дальше владелец просто заменяет текст-заглушку и картинки на свои, а каждая новая запись — это одна новая папка.

Движок и дизайн-макет: что именно доставляется

Самая частая путаница — «а где же дизайн». Дизайн живёт в движке (engine) — это общий слой отрисовки, который доставляется в проект один раз (copy-if-absent) и переиспользуется всеми разделами. Он версионируется «бок о бок»: новый мажор кладётся РЯДОМ (lib/content-v2), не перетирая старый, а каждый раздел «прибит» к своей версии — поэтому старые новости не сломаются, когда приедет новый движок. Вот из чего состоит движок:

Файл движкаЗа что отвечает
components/content-page/standard-content-page.tsxХром страницы — общий каркас/макет: заголовок, хлебные крошки, оглавление, FAQ, блок основателя, ссылки.
components/content-page/post-body.tsxРисует тело статьи из «блоков» (см. ниже).
lib/content/blocks/{types,registry,inline}.tsxСловарь блоков (типы) + сопоставление блок→рендерер + разметка внутри текста (жирный, ссылки).
lib/content/create-content-post.tsxФабрика страницы: по формату (news/blog/document) выбирает тип JSON-LD и тип автора, собирает метаданные, OpenGraph и три блока структурированных данных, и рендерит через StandardContentPage.
lib/parser-fs.mjsШов A для files: сканирует файлы на сборке и строит список «детей» раздела.
lib/auth-guard/route-guard.client.tsxАспект ролей: гейт доступа, который сохраняет статичность страницы; на чужой роли показывает локализованный тост «доступ запрещён».
lib/{brand,author,languages}.ts · lib/seo/alternates.tsЛичность сайта: имя бренда, автор, набор языков, SEO-альтернативы. Читается из переменных окружения → переносимо в любой стартер. НЕ версионируется.
app/[lang]/error.tsx · app/global-error.tsxГраницы ошибок (прививка против падений): любой сбой в дереве [lang] деградирует мягко, а не «голым 500».

Из чего собран дизайн — словарь блоков

Дизайн страницы — данные, а не код. Автор (человек или агент) описывает страницу набором «блоков», а движок их рисует. Это и есть гибкость макета: чтобы изменить страницу, меняют данные блоков, а не вёрстку. Вот основные блоки (полный список — в lib/content/blocks/types.ts):

Блок (kind)Что рисует
p · h2 · h3Абзац и заголовки разделов.
list · olistМаркированный и нумерованный списки.
quote · note · calloutЦитата, короткая ремарка, врезка-«а вы знали».
tableСравнительная таблица, статическая, работает без JS (последний столбец выделен).
figure · code · ctaКартинка/видео с подписью, блок кода, кнопка-ссылка.
founderКарточка основателя (фото, имя, роль, соцссылки) — фирменная врезка бренда.
docrefКарточка-ссылка на полный документ с кнопкой скачивания.
columns · groupКонтейнеры: колонки и группировка. Внутрь вкладывается ЛЮБОЙ блок (включая другой контейнер) — рекурсивно.

Отдельно работают пресеты формата (параметр format): news, blog, document — это один и тот же движок, но с разным типом структурированных данных (NewsArticle / BlogPosting / TechArticle) и типом автора (человек / организация). Поэтому «новость», «пост блога» и «страница документации» выглядят семейно, но корректно размечены каждый под свой смысл.

Насколько гибкий макет и как его настроить или адаптировать

Главный вопрос: можно ли это гнуть под другие шаблоны. Да — и есть несколько уровней настройки, от простого к глубокому:

  1. Содержимое и порядок страницы — меняются данными блоков (добавить/убрать/переставить блоки), без касания кода.
  2. Личность и стиль бренда — имя, автор, языки, SEO — берутся из переменных окружения (lib/brand, lib/author, lib/languages), поэтому один движок выглядит «своим» на любом сайте.
  3. Новый тип секции (например, особый вид карточки) — это добавить член в lib/content/blocks/types.ts + рендерер в registry.tsx; существующие страницы не трогаются.
  4. Изменить сам макет — правят движок В СТОРЕ (единый источник), а не копию; версионирование «бок о бок» гарантирует, что уже собранные разделы не сломаются.
  5. Адаптировать под ДРУГУЮ структуру (документация по категориям — глубина 2; каталог из БД — источник db-at-build) — это НЕ переписывание ядра, а «заморозка нового кирпича»: новый шаблон вкладки и/или новый поставщик списка. Ядро конструктора закрыто для правок и открыто для расширения (Open/Closed).

Не хватает элементов дизайна? Как добавить новый блок

Реальный сценарий: вы оформляете страницу и понимаете — нужного элемента в словаре блоков нет (например, аккордеон, галерея-сетка, таймлайн). Вот что делать по порядку.

Если нужного вида действительно нет — добавить новый блок это ровно два файла (так и написано в самом каталоге: «add a member here + a renderer in the registry, nothing else»):

  1. ТИП. В lib/content/blocks/types.ts добавить член в union LeafBlock (простой блок) или ContainerBlock (если внутри будут другие блоки) — например { kind: 'gallery'; images: { src: string; alt: string }[] }.
  2. РЕНДЕРЕР. В lib/content/blocks/registry.tsx добавить ОДНУ запись в карту BLOCK_RENDERERS по этому kind — функцию, возвращающую JSX. Текст внутри полей пропускают через inline() из ./inline.tsx, чтобы работали жирный и ссылки. Контейнер рендерит детей рекурсивно через ctx.renderBlocks — поэтому внутрь можно вложить любой блок.
  3. Всё. Больше ничего менять не нужно: PostBody — тонкий диспетчер над этой картой; страницы и фабрика create-content-post подхватят новый блок автоматически.

Где именно добавлять — зависит от того, кому блок нужен:

Куда добавитьКто получит блок
FES — lib/content/blocks/ (витрина)Только маркетинг-сайт самой платформы (его новости/блог/документация).
Движок в сторе — services/data/frozen-templates/engine/lib/content/blocks/ВСЕ будущие собранные разделы в стартерах: composer доставит обновлённый движок в каждый слот (с учётом версионирования).

А если «не хватает» — это не один блок, а целый вид структуры (интерактивный калькулятор, лента на пользователя, дашборд) — это уже не уровень блока. Граница простая: блок — это кусок ОДНОЙ страницы; примитив — это целая СТРУКТУРА из страниц. Если это всё ещё композиция статичных частей — делаете контейнер-блок; если нужна другая топология, источник данных или динамика — это новый примитив конструктора (харвест кирпича) или классическая разработка.

Кто это делает: добавление блока — обычная разработка (агент или человек). Если возможность запрашивают через консультанта/Гермес и её ещё нет — агент честно об этом скажет и предложит завести её (черновик через propose-new-agent-skill-or-mcp или новый шаг), а не выдумает на лету. Запись файлов в защищённом режиме выполняет роль architect.

Как замороженный шаблон работает с SEO-данными

SEO-данные (теги `<title>`/`description`, `hreflang`, canonical, OpenGraph, Twitter-карточка, структурированные данные JSON-LD) не пишутся руками и не дублируются в каждой странице. Они рождаются на сборке из типизированных данных страницы через специальную функцию `generateMetadata`. Это критично для мультиязычных страниц: без корректного `hreflang` Google считает языковые версии конкурирующими дублями. Разберём по атрибутам — отдельно для страницы-поста и страницы-роутера, с именами компонентов.

Откуда берутся данные — типизированная сущность поста

Каждый пост — это co-located папка с данными `_data/{meta,en,ru,index}.ts`. Резолвер `{{tab}}Post(data, lang)` (в `_lib/post.ts`) собирает из них типизированную сущность `ContentPost` под нужный язык: `title`, `seoTitle`, `description`, `keywords`, `tags`, `date`, `readingMinutes`, `ogImage`/`heroImage`, `blocks`, `faq`, `inLanguage`. Именно эта типизированная сущность на сборке превращается в мета-записи — то самое, о чём была гипотеза.

Страница-пост (/blog/post123) — функция generateMetadata фабрики

Тонкий `page.tsx` реэкспортит `generateMetadata` из `_components/index.tsx`; тот вызывает фабрику `createContentPost({ format, subPath, resolve, chrome, titleSuffix })` (компонент `lib/content/create-content-post.tsx`), которая и возвращает `generateMetadata`. На сборке, для каждого языка, она строит из `resolve(lang)` такие атрибуты:

Атрибут (Next Metadata)Откуда / чтоКомпонент
title`${seoTitle} | ${titleSuffix(lang)}` → `<title>`/og:titlecreate-content-post
descriptionиз `ContentPost.description`create-content-post
keywordsесли задан в данных постаcreate-content-post
alternates`buildAlternates(lang, subPath)` → canonical + hreflanglib/seo/alternates
robotsindex: true, follow: truecreate-content-post
openGraphtitle/description/url/type=article/publishedTime/imagescreate-content-post
twittersummary_large_image + title/description/imagescreate-content-post

Картинку сниппета выбирает `snippetImage()`: берёт `ogImage`, иначе видимый `heroImage`, и делает URL абсолютным через `abs()` (`BRAND.siteUrl`). Правило: если у поста есть любая картинка — она обязана попасть в сниппет.

Структурированные данные (JSON-LD) — в самом компоненте страницы

Помимо мета-тегов, функция `Page` той же фабрики печатает `<script type="application/ld+json">` с тремя блоками: (1) Article — тип по формату (`NewsArticle`/`BlogPosting`/`TechArticle` через карту `JSONLD_TYPE`), с `headline`/`description`/`datePublished`/`inLanguage`/`author`/`publisher`/`image`/`keywords`; автор — `Person`+`sameAs` или `Organization` (по карте `AUTHOR_KIND`); (2) BreadcrumbList (хлебные крошки); (3) FAQPage — если у поста есть `faq`. Тип статьи и тип автора — единственная реальная разница между форматами news/blog/document; всё остальное общее.

Страница-роутер (/blog) — своя generateMetadata

У индекс-страницы раздела — своя `generateMetadata` в её `_components/index.tsx`: `title`/`description` берутся из типизированного `get{{TAB_PASCAL}}Ui(lang)` (UI-хром вкладки), `alternates: buildAlternates(lang, "/{{TAB}}")`, и собственные `openGraph`/`twitter` (заголовок/описание раздела + лого бренда как картинка, тот же origin `BRAND.siteUrl`) — чтобы при шеринге `/{{TAB}}` карточка была «раздельной», а не сайтовой. В теле страницы печатается `BreadcrumbList` JSON-LD.

hreflang и canonical — компонент buildAlternates

Ключевой SEO-компонент — `buildAlternates(lang, subPath)` (`lib/seo/alternates.ts`). Он возвращает: canonical = сама страница (self-canonical, а не кросс-канонический на английский — это и есть защита от схлопывания версий), и `languages` = `x-default` + все языки слота абсолютными URL. `subPath` передаёт вызывающая страница: пост — `/{{TAB}}/<slug>`, роутер — `/{{TAB}}`, поэтому каждая страница указывает на свой реальный URL. Набор языков — из единого авторитета (`lib/languages` ← `NEXT_PUBLIC_SUPPORTED_LANGUAGES`).

Почему hreflang не «повисает» — generateStaticParams по языкам

Чтобы `hreflang`-ссылки указывали на реально существующие страницы, все языки должны быть пререндерены. За это отвечает `app/[lang]/layout.tsx` слота: `generateStaticParams()` = `SUPPORTED_LANGUAGES.map(lang => ({ lang }))` — перечисляет все языки из того же единого источника. Так как это на уровне `[lang]`-layout, оно покрывает весь поддерев, включая собранные табы. `revalidate` даёт ISR; язык не из набора → `notFound()`. Значит рекламируются (через hreflang) ровно те языки, что и пререндерятся.

Закон двух слотов — почему это масштабируется

Чтобы сборка не превратилась в «тысячу шаблонов под каждую комбинацию», действует строгий закон: любое свойство структуры лежит РОВНО в одном из двух слотов и они НЕ взаимодействуют между собой.

СлотЧто держитПримеры
A — поставщик списка (данные)Откуда берутся «дети» разделаскан файлов · запрос к БД на сборке · (в будущем) рантайм
B — единообразный аспект (макет)Сквозное правило, встроенное ОДИНАКОВО на каждом уровнеязыки (i18n) · роли/доступ · (в будущем) тема

Смысл закона: стоимость растёт аддитивно, а не как произведение всех комбинаций. Если фиче нужен особый случай «по глубине» или «по источнику» — это запах плохого дизайна, и его отвергают, а не подгоняют конструктор под него.

Честный отказ — когда конструктор говорит «нет»

Если запрос не ложится на готовый кирпич на 100% — конструктор не подгоняет плохой вариант и не сочиняет код. Он честно называет ось, по которой не сошлось, и предлагает один из двух путей: (а) предложить архитектору заморозить новый кирпич — но только если форма проверена живой разработкой и повторяется; (б) классическая разработка в рамках архитектуры. Примеры:

  1. «документация по категориям» → глубина 2 → отказ по оси depth (кирпича глубины 2 пока нет) → предложить харвест/классику;
  2. «каталог товаров из базы» → источник db-at-build → отказ по оси source (поставщик объявлен, но ещё не заморожен);
  3. «живой дашборд» → данные на пользователя/запрос → отказ по оси rendering/source → классическая разработка;
  4. «новости, но только для залогиненных» → files, глубина 1 + аспект roles → СОБИРАЕТСЯ (files-depth1 с --roles user, гейт встраивается единообразно).

Что это даёт и куда развивать

Итог: «добавь раздел на сайт» превращается из аккуратной задачи по коду в просьбу, которую может сделать кто угодно, а результат одинаков каждый раз, на собственном сервере владельца. Дальше базис растёт по одному проверенному кирпичу (документация по категориям, каталог из БД на сборке, динамический дескриптор) — каждый въезжает, когда докажет себя. Эволюция этой идеи из первого прототипа подробно описана в заметке Конструктор замороженных шаблонов, а смежная тема многоязычного контента — в архитектуре мультиязычного контента.

Продолжение: не только создать раздел, но и изменить и удалить — тоже без кода

Собрать раздел — это полдела. Дальше им нужно жить: добавлять статьи, править тексты и переводы, убирать устаревшее, переименовывать саму секцию. Раньше это снова означало ручную правку файлов агентом — тот самый «код руками», от которого мы уходим. Теперь весь жизненный цикл контента закрыт одним инструментом — owner_content_manage_collection (MCP-мост content-crud-bridge, порт 3226). Принцип тот же, что у конструктора: агент описывает ДАННЫЕ — инструмент пишет файлы, без генерации кода, одинаково у любой модели. Описать содержимое статьи — это не программирование; собирать из этого файлы — работа инструмента.

Одна операция параметризуется двумя осями: что делаем (создать / изменить / удалить) и над чем (группа-вкладка / страница-пост). Шесть сочетаний — один инструмент:

Операция × цельЧто происходитЧто передаёт агент
создать группуЗаводит новую вкладку (делегирует Конструктору замороженных шаблонов выше)имя, формат, языки, метки
добавить страницуНовая папка поста из вашего контента; список постов пересобирается самданные: заголовок, блоки, FAQ + переводы
изменить страницуПереписывает содержимое поста (каркас маршрута не трогает)новые данные поста
изменить группуМеняет «хром» вкладки: заголовок, хлебные крошки, меткилокализованные UI-строки
удалить страницуУбирает папку поста; список пересобираетсяадрес поста
удалить группуУбирает всю вкладку и её строку регистрацииимя вкладки

Чтобы добавленная страница не оказалась бракованной (это реальный класс ошибок, на котором уже спотыкались), перед записью срабатывают гейты целостности — и если что-то нарушено, операция честно отклоняется, а не выпускает поломку наружу:

  • имя папки строго равно адресу (slug) — иначе ссылка в списке ведёт на несуществующую страницу (404);
  • язык-перевод только из набора сайта (английский + настроенные) — нельзя протащить язык, которого на сайте нет (например, испанский вместо русского); нужен новый — сначала добавьте его в настройках и пересоберите;
  • скан на «инородные символы» (иероглифы, арабица и т.п.), случайно попавшие в русский или английский текст — частый артефакт модели;
  • карточка основателя — только последним блоком (это подпись бренда, а не заметка по тексту);
  • обязательная ссылка-якорь на корень сайта внутри текста;
  • жёсткий гейт неподставленных токенов — как и у конструктора.

И ещё одно: каждая операция по завершении фиксируется в специальной таблице Deployment (deployment_records) — что изменено, кем, какой страницы касается. Это журнал развёртываний, видимый в админке; он — структурная часть конвейера, а не то, что агент должен «не забыть» сделать.

Почему агенту «нельзя программировать» — и что это значит на самом деле

У оркестратора (Гермес) и у любого агента есть жёсткое правило: они не программируют. Важно понять его правильно — это не значит «нельзя менять сайт». Менять сайт можно и нужно, это и есть работа. Значит оно вот что: любое изменение делается вызовом инструмента, а не правкой файлов руками. Контент — через owner_content_manage_collection; целый раздел — через Конструктор; настройки — через инструмент настроек; «сделать видимым» — через пересборку. Нет подходящего инструмента — агент не пишет код наугад, а делегирует кодящему агенту или предлагает завести новую способность. Так даже самая слабая модель не «застывает» в духе «я не умею писать код», а уверенно действует через инструменты.

Всё это — самодостаточно: и навык manage-content-collections, и сам инструмент доставлены во ВСЕ шесть агентов, поэтому проект с единственной моделью (без Гермеса и без памяти) тоже умеет полный цикл. Это первый из восьми сценариев работы с контентом (четыре по файлам, четыре по базе данных) — и шаблон для остальных.

Я хочу, чтобы собирать сайт было как разговаривать, а не как собирать мебель. Конструктор — это место, где вы говорите, чего хотите, и оно появляется: правильное, ваше и на вашей машине. А этот документ — чтобы вы видели, как именно, и могли вести проект дальше осознанно.

Roma Armstrong photoRoma ArmstrongFounder at Fractera.ai

Частые вопросы

ИИ-модель пишет код для нового раздела?
Нет. Композер copy-frozen-template.mjs только копирует проверенные файлы и подставляет значения (название раздела, языки, метку). Кода не генерируется — поэтому любая из шести моделей даёт одинаковый результат.
Где физически лежат шаблоны и движок?
В закрытом сторе data-сервиса: services/data/frozen-templates/ (registry.json + engine/ + primitives/ + providers/ + aspects/). Он отдаётся только для чтения по адресу :3300/frozen-templates/tree. В стартере FNS лежит только навык и композер, самих шаблонов там нет.
Почему после сборки я сразу не вижу раздел?
Слот работает в продакшен-режиме: новые файлы видны только после пересборки. Её делает инструмент owner_deploy_rebuild_slot (или кнопка «Deploy» в подвале) — это next build + перезапуск + проверка здоровья.
Насколько можно менять дизайн?
Сильно. Содержимое страницы — это данные из блоков (меняются без кода). Новый тип блока добавляется в lib/content/blocks/types.ts + registry.tsx. Сам макет правится в движке (в сторе), а версионирование «бок о бок» не даёт сломать уже собранные разделы.
Как сделать раздел другой структуры — например, документацию по категориям?
Это другая клетка сетки (глубина 2). Сегодня готов один кирпич — files-depth1 (плоский список). Новую структуру «замораживают» отдельным кирпичом, когда форма проверена живой разработкой и повторяется; до тех пор — классическая разработка. Ядро конструктора при этом не переписывается.
Как замороженные страницы не попадают под санкции Google за дубли языковых версий?
Каждая страница на сборке через generateMetadata получает hreflang (все языки) и self-canonical (сама на себя) из компонента buildAlternates, а generateStaticParams в [lang]/layout пререндерит все языки из того же источника (NEXT_PUBLIC_SUPPORTED_LANGUAGES). Поэтому Google видит легитимные переводы одной страницы, а не конкурирующие дубли, и hreflang не указывает на несуществующую (404) версию.
Что делать, если для оформления не хватает готового блока?
Сначала попробуйте собрать нужное из существующих блоков и контейнеров (columns/group вкладываются рекурсивно). Если вида действительно нет — добавьте новый блок двумя файлами: член типа в lib/content/blocks/types.ts + рендерер в lib/content/blocks/registry.tsx. Чтобы блок стал доступен всем будущим разделам стартеров, добавьте его в движок в сторе (services/data/frozen-templates/engine). Это аддитивно и не ломает существующие страницы.
Новые страницы работают без JavaScript?
Да. Раздел статический и полностью читается с выключенным JavaScript. Даже аспект ролей сделан так, чтобы сохранить статичность.
Спросите у ИИ