Идеи
Product Digital Twin/design/mvp-cli.md

MVP-архитектура CLI twin (v0, черновик)

Цель MVP: за 1–2 недели получить пользу без сложного графа. Принцип «буханки»: только чтение файлов и TS AST, без базы данных и без LLM.

Поток

passports (*.md) ─┐
payload-types.ts ─┼─► extract ─► graph (in-memory) ─► checks ─► report (md/json)
blocks/*/config ──┤                  │
components (*.tsx)┘                  └─► graph.cache.json (для diff между запусками)

Модули

Модуль Что делает Как
extract/passports Читает frontmatter паспортов gray-matter + zod-схема паспорта
extract/types Берёт интерфейсы блоков из payload-types.ts: поля, optional, union, relation ts-morph (TS AST)
extract/code Находит, какие поля блока реально читает компонент (props.title, деструктуризация) и его импорты ts-morph
extract/tests Ищет ссылки REQ-* в тестах (в названиях или комментариях) regex
graph Узлы: Requirement, Field, BlockType, Component, Test. Рёбра: implements, reads, covers, imports простые Map
checks Правила, см. ниже чистые функции (graph) => Finding[]
report twin-report.md и --json для CI, ненулевой код выхода при ошибках —
cache Сохраняет граф в graph.cache.json; twin diff сравнивает с прошлым JSON

Проверки MVP (по приоритету)

  1. Контракт полей: поле из паспорта отсутствует в payload-types.ts или отличается тип/required → error.
  2. Поле, которое компонент не читает: есть в типе, но компонент его не использует → warn (мёртвое поле или забытый рендер).
  3. Чтение несуществующего поля: компонент читает поле, которого нет в типе. Обычно ловит tsc, но мы показываем, какое требование это ломает.
  4. Требование без теста: tests: [] → warn.
  5. Блок без паспорта / паспорт без блока → warn.
  6. Загрязнение: импорт компонента попадает под deps.deny → error. (Можно отложить на v2.)

Impact analysis

twin impact <id|path>, например twin impact hero.cta или twin impact REQ-hero-02: обход графа от узла и список затронутых компонентов, тестов и требований.

Про payload-types.ts как «естественный граф» (кратко, критично)

  • Плюс: это генерируемый источник правды о схеме. Relation-поля дают рёбра между коллекциями, а union блоков в layout даёт список всех блоков.
  • Минус: условная логика админки (admin.condition) в типы не попадает. Отсюда required_if в паспорте и чтение config.ts в v2.
  • Минус: если забыть payload generate:types, типы устаревают. Поэтому twin сначала сверяет дату изменения типов с конфигами или сам запускает генерацию.
  • Минус: relation приходят как string | Doc в зависимости от depth. Проверка должна это учитывать, иначе будут ложные срабатывания.
  • Вывод: типы годятся как основа графа данных, а бизнес-смысл и условия держат паспорта.

Стек и структура

Node + TypeScript, ts-morph, gray-matter, zod, commander. Запуск: npx twin check локально и в CI.

twin/
  src/extract/{passports,types,code,tests}.ts
  src/graph.ts  src/checks/*.ts  src/report.ts  src/cache.ts  src/cli.ts

Порядок работ

  1. Схема паспорта (zod) + 2 паспорта на реальных блоках.
  2. Извлечение из payload-types.ts + проверка 1. Уже приносит пользу.
  3. Извлечение из кода + проверки 2–3.
  4. Тесты и impact.
  5. Кэш, diff, deps — v2.