Формат feature_passport.md (v0, черновик)
Один паспорт на один блок или фичу. Лежит рядом с блоком: src/payload/blocks/<block>/passport.md.
Frontmatter машиночитаемый (его читает CLI), тело свободное, для людей.
---
id: hero-section # уникальный, kebab-case
status: active # draft | active | deprecated
owner: frontend # кто отвечает
block:
slug: hero # slug блока в Payload (Block.slug)
config: src/payload/blocks/hero/config.ts
type: HeroBlock # имя интерфейса в payload-types.ts
requirements: # бизнес-слой: каждое требование с id
- id: REQ-hero-01
text: "Заголовок секции — H1 только на первой секции страницы"
source: seo # business | seo | design | legal
fields: [title, isFirst] # какие поля блока его реализуют
tests: [e2e/hero.spec.ts#h1-only-first]
- id: REQ-hero-02
text: "CTA обязателен, если включён variant=promo"
source: business
fields: [variant, cta]
tests: [] # пусто = требование не покрыто тестом (CLI предупредит)
fields: # ожидаемый контракт данных (сверяется с payload-types.ts)
title: { type: string, required: true }
isFirst: { type: boolean }
variant: { type: enum, values: [default, promo] }
cta: { type: relation, to: links, required_if: "variant == promo" }
code: # инженерный слой
components: [src/components/blocks/Hero/Hero.tsx]
hooks: []
deps: # разрешённые зависимости (для проверки «загрязнения»)
allow: ["@/components/ui/*", "@/lib/format"]
deny: ["@/context/*", "@/domains/*"] # блок не должен тянуть глобальный контекст
seed: seed/pages/home.json#/layout/0 # опорный пример данных
---
## Заметки
Свободный текст: история решений, ссылки на задачи, «SEO-подрядчик попросил H1 → H2 (2026-10)».
Правила
requirements[].id— стабильные, на них ссылаются тесты и коммиты.fieldsописывает ожидание бизнеса,payload-types.ts— реальность. Расхождение между ними и есть находка.- В MVP хватает полей
id,block,requirements,fields,code. Поляdepsиseedможно добавить позже.