using System;
using Ban.Sdaid.Notation.Documents;
using Ban.Sdaid.Icd;
namespace Ban.Sdaid.Icd.Methodology
{
/// <summary>
/// Методология ведения спецификации ICD. Базовые положения о том, как и для кого
/// мы пишем эту спеку. Документ внутри проекта ICD (а не методологии-as-product) —
/// ICD-spec самодостаточна, методология описана здесь же.
/// </summary>
public class IcdMethodology : ISdaidDocument, IFolder<MethodologyFolder>
{
public string Name => "Методология";
public string Description =>
@"Этот документ описывает, как организована спецификация проекта ICD, где что лежит и как с ней работать — разработчику, ИИ-ассистенту и любому, кто читает спеку впервые.
Спека — **источник правды** для устройства системы. Цель — не подробное руководство, а такой набор фактов, который не противоречит коду, не устаревает быстро и собирается в одно место для понимания.";
public string Version => "0.1";
public string Status => "draft";
public string[] Comments => new string[0];
// ═══════════════════════════════════════════════════════════════════════════
// Inline-body chunks — каждая бывшая ISdaidDocumentSection стала public static
// string-полем. Compile-safe ссылки извне:
// nameof(X).
// ═══════════════════════════════════════════════════════════════════════════
public static string M_S1_SpecFirst = """
## Spec first
Спецификация — **источник правды**. Изменение поведения системы начинается с изменения спеки, не с правки кода. Реализация догоняет спеку, не наоборот.
> *Раскрывается по мере проработки.*
""";
// public class M_S1_SpecFirst123 : IInlineSection {
// public static string Name = "Spec first";
// // public string Name => "Spec first";
// public static string M1 = "123";
// public static string Body => """провекр формрование раздела через инлайн класс""";
// public static string M2 = "456";
// }
// public class M_S1_SpecFirst124 {
// public static string Name = "Spec first2";
// // public string Name => "Spec first";
// public static string M1 = "123_2";
// public static string Body => """
// ## Heder1111
// провекр формрование раздела через инлайн класс_2
// """;
// public class M_S1_SpecFirst125 {
// public static string Body => """
// ### Heder2222
// asd asd as das
// """;
// public static string M2 = "456_3";
// }
// public static string M2 = "456_2";
// }
public static string M_S2_AiFirstOptimized = """
## AI first + optimized
ИИ-агент — **первоклассный читатель** спеки наравне с человеком. Формат, размеры порций, способ ссылаться — выбираются так, чтобы агент мог их потреблять без потерь. Оптимизация под агента — это compile-safe идентификаторы, плоские ссылки `nameof()`, отсутствие неявных соглашений «из контекста».
> *Раскрывается по мере проработки.*
""";
public static string M_S3_CSharpCompilable = """
## C# compilable
Спецификация оформлена как **компилируемый C#-проект**. Это даёт линтер: ссылка на несуществующий артефакт ломает сборку, переименование через F2 правит все упоминания, find-references показывает обратные связи без отдельных индексов.
> *Раскрывается по мере проработки.*
""";
public static string M_S4_HumanReadable = """
## Human readable
C#-формат не отнимает у человека читаемости. Генератор превращает классы в навигируемый HTML-сайт с прозой, таблицами, диаграммами и кликабельными ссылками между артефактами. Любой раздел доступен по стабильному URL и пригоден для копи-пейста в чат, обсуждение, ревью.
> *Раскрывается по мере проработки.*
""";
public static string M_S5_ProseMaterialization = """
## Материализация прозы
Любой кусок прозы, на который команда уже ссылается («FR-301», «БП-3», «журнал оцифровки», «карточка информационной карты») — кандидат на **материализацию** в отдельный класс-артефакт. До материализации это текст; после — адресуемая сущность с собственной страницей, секциями, обратными ссылками. Vision-таблицы — пример прогрессивной материализации: текст «FR-301» в ячейке становится ссылкой на `IFunctionalRequirementDocument`-класс ровно тогда, когда требование берётся в проработку.
> *Раскрывается по мере проработки.*
""";
public static string M_OneFactRule = """
## Правило одного факта
**Каждый факт описан ровно в одном месте.** Если он нужен в двух — второе место это **ссылка** (typed-reference, не копия).
Из этого правила следует много частностей:
- Глоссарий — один на проект; vision/use-case/handler ссылаются на термины через `nameof()`, не пересказывают определения.
- Domain-сущность — в одном bounded context; другие места ссылаются через `nameof()`.
- Описание операции — в handler-spec; endpoint, service, use-case ссылаются на него, не дублируют narrative.
- Acceptance criteria — на одном уровне (эпик или use-case); не дублировать на оба.
- Open question (OQ-N) — в одном месте (readme эпика); use-case/page/handler ссылаются.
- Status, version, comments — в самом артефакте; никаких «параллельных трекеров».
> 🔍 Признак нарушения: если **поиск по точной фразе** в проекте даёт ≥2 result-а в разных артефактах — это дрейф. Нужно: оставить ровно один источник правды, остальные превратить в typed-ссылку (`nameof`, `SdaidAnchors.RefTo<T>(text)`, `GlossaryAnchors.RefTo<T>(text)`).
""";
public static string M_CustomerLanguage = """
## Язык прозы: спека читается заказчиком
Спецификация — не только внутренний документ. Её разделы цитируют в переписке, переносят в
документы для заказчика, показывают на согласованиях. Заказчик знает «информационную карту»,
«пакет ввода», «уверенность распознавания» — и не обязан знать `InformationMap`, `InputPacket`,
`RecognitionConfidence`.
**Правило: идентификаторы классов не выводятся в прозу.**
| Что | Как писать |
|---|---|
| Термин глоссария | `GlossaryAnchors.RefTo<Term>("русская словоформа")` — ссылка типизированная, на экран идёт заданный текст, склоняемый по месту |
| Ссылка на артефакт (FR, ADR, эпик, страница) | по-русски в тексте; код — в блоке «Связанные артефакты» в конце документа |
| Технические таблицы (трассировка, структура) | коды допустимы: эти таблицы адресованы разработчику |
Почему не «просто `nameof()` везде»: `nameof()` даёт compile-safe ссылку, но **отображается
именем класса**. Это правильный инструмент для трассируемости и неправильный — для текста,
который читает человек со стороны заказчика.
Разделение «этот документ увидит заказчик, а этот нет» на практике не держится: куски
цитируют и переносят. Поэтому правило действует на всю спеку, а не на её «внешнюю» часть.
### Блок «Связанные артефакты»
Ссылки, вынесенные из прозы, собираются в конце документа отдельной секцией:
```
## Связанные артефакты
- {nameof(Ban.Sdaid.Icd.Requirements.FR_003_AttributeExtraction)} — Сегментация и извлечение атрибутов
```
Полное имя типа в `nameof()` избавляет от лишних `using`, а обратные связи генератора
строятся по тем же упоминаниям, что и раньше.
""";
public static string M_S6_FolderStructure = $"""
## Что куда писать / структура папок
Где живёт vision, глоссарий, требования, эпики, доменные сущности, архитектурные решения — фиксируется явно. Цель: и человек, и ИИ-агент однозначно знают, куда положить новый артефакт и где искать существующий — без догадок.
### Принципы (зачем именно так)
**Один домен — `icd`.** Вся предметная область — единый домен. Чтобы группировать сущности, домен поделён на **доменные модули** (`50_Domain/<Module>/`: Capture, Recognition, Verification, …). Это группировка по смыслу, а не отдельные Bounded Contexts. Поэтому контракт API и UI-спека живут **на корне спеки** (`45_Api`, `65_Ui`), а не внутри доменных модулей.
**Слои — горизонтальные, на корне.** Каждый слой живёт в своём top-level разделе: контракт API — `45_Api`, домен — `50_Domain`, поведение бэка — `55_Backend`, эпики — `60_Epics`, UI-спека — `65_Ui`. Внутри раздела — группировка по области/эпику (`Endpoints/<Area>/`, `Handlers/<Area>/`, `Pages/<Area>/`).
**Эпик — тонкая нить.** `60_Epics/<Epic>/` собирает сквозной контекст (use-case-ы, локальные решения, AC, state-machine-ы) и ссылается на артефакты слоёв через `nameof()`. Реализационные артефакты use-case-а (endpoint, handler, страница) лежат в своих слоях, не в подпапках use-case-а.
**Реализация — `80_Subsystems`, по необходимости.** Особенности конкретной реализации деплой-единицы (ADR подсистемы, нюансы стека) — в `80_Subsystems/`. Если особенностей нет — раздел не заводится. Инфраструктурные контракты (file/MQ/integration) — в `70_Infrastructure/`, тоже по необходимости (сейчас файловое хранилище живёт в `55_Backend/Files`).
### Структура (верхнеуровневые разделы)
```
00_Methodology/ как работать со спекой
00_Migration/ инструкция миграции md→SDAID (временная, пока идёт перенос)
10_Vision/ слабоструктурированный вход от заказчика
20_Glossary/ общий проектный словарь
30_Requirements/ FR — материализуются по мере проработки
40_Arch/ системные кросс-cutting решения
ADR/ общесистемные ADR
Tech/CSharp/ техно-ADR backend
Tech/Angular/ техно-ADR frontend
45_Api/ контракт API
Based/ общие базовые типы (BasedCommand, BasedQuery, …)
Endpoints/<Area>/ endpoint + Command/Query + Result
50_Domain/ домен icd, поделён на доменные модули
<Module>/
BC_<Module>.cs overview модуля (IBoundedContextModuleDocument)
<Entity>.cs сущности и агрегаты
55_Backend/ поведение бэка
Handlers/<Area>/ canonical narrative операций (IHandlerDocument)
Services/<Area>/ доменные / прикладные сервисы
Import/, Files/ сервисы импорта, файловое хранилище
60_Epics/ бизнес-направления (тонкая нить)
<Epic>/
EPIC_<Epic>.cs цель, акторы, scope, открытые вопросы, AC
UseCases/<UC>/ use-case-документы (Given/When/Then), state-machine-ы
65_Ui/ UI-спека
Pages/<Area>/ страницы (IPageDocument)
Componetns/, Layout/ компоненты и layout-ы
70_Infrastructure/ [при необходимости] инфраструктурные контракты (file/MQ/integration)
80_Subsystems/ [при необходимости] особенности реализации деплой-единиц
```
### Папка → kind-маркер (что внутри какой папки)
| Папка | Kind-маркер класса | Что внутри |
|---|---|---|
| `00_Methodology/` | `ISdaidDocument` (без подтипа) | Документ методологии проекта (этот файл) |
| `00_Migration/` | `ISdaidDocument` (без подтипа) | HowTo миграции md→SDAID (временно) |
| `10_Vision/` | `IVisionDocument` | Vision-документ (один) |
| `20_Glossary/Terms/` | `IGlossaryTerm` (отдельный контракт, не ISdaidDocument) | Термины проекта, по одному классу на термин |
| `30_Requirements/` | `IFunctionalRequirementDocument` | FR-документы (материализуются по мере проработки) |
| `40_Arch/ADR/` | `IAdrDocument` | Общесистемные ADR |
| `40_Arch/Tech/<Stack>/` | `IAdrDocument` | Техно-ADR (CSharp, Angular) |
| `45_Api/Endpoints/<Area>/` | `IApiContractDocument` (+ `IFolder<ApiFolder>`) | Endpoint + Command/Query + Result |
| `45_Api/Based/` | `ISdaidDocument` | Общие базовые типы команд/запросов |
| `50_Domain/<Module>/` (overview) | `IBoundedContextModuleDocument` | Описание доменного модуля |
| `50_Domain/<Module>/<Entity>.cs` | `IDomainEntityDocument` | Сущности и агрегаты (агрегаты пока без отдельного маркера) |
| `55_Backend/Handlers/<Area>/` | `IHandlerDocument` (+ `IFolder<BackendFolder>`) | Canonical narrative операции |
| `55_Backend/Services/<Area>/`, `Import/`, `Files/` | `ISdaidDocument` / `IDomainServiceDocument` (+ `IFolder<BackendFolder>`) | Сервисы реализации, импорт, файловое хранилище |
| `60_Epics/<E>/` (readme) | `IEpicDocument` | Цель эпика, акторы, scope, открытые вопросы, AC |
| `60_Epics/<E>/UseCases/<UC>/` | `IUseCaseDocument` (с `PrimaryActor`) | Given/When/Then narrative |
| `60_Epics/<E>/UseCases/<UC>/StateMachines/` | `IStateMachineDocument` | State machine с mermaid в Body |
| `65_Ui/Pages/<Area>/` | `IPageDocument` (+ `IFolder<UiFolder>`) | Спека страницы |
| `65_Ui/Componetns/`, `Layout/` | `ISdaidDocument` / `IUiSpecDocument` (+ `IFolder<UiFolder>`) | Компоненты, layout-ы |
| `70_Infrastructure/<Capability>/` | `IInfrastructureDocument` | Контракты на file/MQ/integration (по необходимости) |
| `80_Subsystems/<S>/` | `IAdrDocument` и др. (+ `IFolder<SubsystemsFolder>`) | Особенности реализации подсистемы (по необходимости) |
### Что куда — детально
| Что за артефакт | Где живёт |
|---|---|
| Доменная сущность / агрегат | `50_Domain/<Module>/<Entity>.cs` |
| Endpoint + Command/Query + Result | `45_Api/Endpoints/<Area>/` |
| Handler (canonical narrative) | `55_Backend/Handlers/<Area>/` |
| Доменный / прикладной сервис | `55_Backend/Services/<Area>/` |
| Инфраструктурный контракт | `70_Infrastructure/<Capability>/` (по необходимости) |
| Страница / компонент UI | `65_Ui/Pages/<Area>/`, `65_Ui/Componetns/` |
| Use-case (Given/When/Then) | `60_Epics/<E>/UseCases/<UC>/` |
| State machine | `60_Epics/<E>/UseCases/<UC>/StateMachines/` |
| Системный ADR | `40_Arch/ADR/` |
| Tech ADR (Angular/EF/CSharp) | `40_Arch/Tech/<Stack>/` |
| Особенности реализации подсистемы | `80_Subsystems/<S>/` (по необходимости) |
### Эпик ↔ доменный модуль ↔ слой
**Разные сущности**, связанные через ссылки:
- **Эпик** (`60_Epics/<E>/`) — бизнес-направление, единица планирования; тонкая нить со сквозным контекстом.
- **Доменный модуль** (`50_Domain/<Module>/`) — группа сущностей одного домена icd.
- **Слой** (`45_Api`, `55_Backend`, `65_Ui`) — артефакты реализации по типу.
Один эпик затрагивает ≥1 доменный модуль и артефакты нескольких слоёв; модуль/слой обслуживают ≥1 эпик. Связи трассируются через `nameof()` в обе стороны.
""";
public static string M_S7_ResponsibilityBoundaries = """
## Границы ответственности, контракты и соглашения
Каждый модуль (домен, API, backend, UI) отвечает за свою часть знаний и не лезет в чужую. **На границах модулей** фиксируются явные **контракты** (что один модуль обещает другому) и **соглашения** (как они общаются: имена, формат данных, ошибки, идемпотентность). Изменение контракта — событие, требующее согласования; изменение внутреннего устройства модуля — внутреннее дело модуля.
> *Раскрывается по мере проработки.*
""";
public static string Spec_EpicsAndLayers = """
## Эпики и слои
Эпик — это **бизнес-цель**, реализация которой проходит через несколько слоёв (домен, API, backend, UI).
**Артефакты слоёв первичны** — сущность, страница, endpoint существуют сами по себе и в принципе могут участвовать в нескольких эпиках. Эпик их не «забирает себе», а собирает вместе и добавляет cross-cutting контекст (use-case, state-machine, AC, локальные решения).
Артефакт **физически группируется в подпапку с именем эпика**, если он относится только к этому эпику:
- `Modules/Api/Endpoints/<Epic>/<Name>/` — endpoints эпика.
- `Modules/Backend/Handlers/<Epic>/...` — хендлеры эпика.
- `Modules/Backend/Services/<Epic>/...` — сервисы и алгоритмы эпика (см. [Сервисы и алгоритмы](#сервисы-и-алгоритмы)).
- `Modules/Ui/Pages/<Epic>/...` — страницы эпика.
- `Modules/Domain/` — сущности обычно живут в общем домене (часто переиспользуются между эпиками).
Если артефакт начинает использоваться в нескольких эпиках — он **поднимается на верхний уровень слоя** (без эпик-подпапки) и его эпик-специфичные ссылки выносятся в epic-уровень.
`Epics/<Epic>/` — тонкая нить: epic-документ, use-cases, state-machines, локальные решения, AC. Содержит **только** то, что характерно для пересечений слоёв; не дублирует их.
""";
public static string Spec_LayerToEpicRefs = """
### Ссылки артефактов слоя на эпик
Артефакт, **физически лежащий в `<Epic>/`-подпапке**, может **ссылаться на локальные документы эпика** (`OQ-N` в readme.md, `R-N` в adr.md, AC-* в acceptance.md). Это естественный контекст: пока артефакт привязан к эпику, его комментарии живут в эпик-словаре.
Если артефакт перестаёт быть эпик-специфичным — все ссылки на эпик надо вычистить **до** перемещения наверх. Артефакт без эпика не должен ссылаться на конкретный эпик-readme.
""";
public static string Spec_StructuralLinks = """
## Структурные связи между документами
Зависимости между документами фиксируются **явно** — типизированными структурными связями
(`IHasStructuralLinks` + `Rel.Serves<T>()` / `Rel.Realizes<T>()` / `Rel.Uses<T>()`), а не
только ссылками в прозе. Это отдельный курируемый слой: генератор рендерит его блоком
«Структурные связи» на странице документа и сводит в страницу «Граф связей» (по одной
диаграмме на эпик). **Объявлять связи — обязательная часть спеки**, а не опция: каждый
документ, зависящий от другого, должен нести эту зависимость явно.
### Правило направления — от вариативного к стабильному
Связь объявляется на **более вариативной** стороне и указывает на **более стабильную**.
Стрелки всегда идут «сверху вниз» по шкале вариативности ниже (от того, что меняется часто,
к тому, что меняется редко) — так одно правило объясняет направление всех зависимостей
разом. Обратную подпись («используется» / «реализуется» / «обслуживается») генератор строит
сам на целевой странице.
**Шкала вариативности** (по убыванию; чем выше — тем чаще меняется):
| Понятие | Вариативность | Что гонит изменчивость |
|---|---|---|
| page | 9 | UI: редизайны, перекладка полей и флоу — постоянно |
| use_case | 8 | продуктовый сценарий, меняется вместе с требованиями |
| epic | 7 | стратегическая группировка, тасуется с роадмапом |
| endpoint | 7 | контрактная поверхность; плодится версиями (но опубликованное — липкое) |
| read_model | 6 | форма диктуется запросами и UI |
| consumed_contract | 6 | чужой контракт, меняется вне твоего контроля |
| message (Command/Query) | 5 | внутренний DTO, следует за входами операции |
| domain_event | 5 | публикуется наружу → как контракт, липкий |
| handler | 4 | оркестрация; сама операция стабильна |
| policy | 4 | бизнес-правила — самая подвижная часть домена |
| domain_service | 3 | доменная координация, стабильна вместе с моделью |
| aggregate | 3 | граница консистентности, ядро инвариантов |
| entity | 2 | живёт внутри агрегата |
| value_object | 1 | примитивы-бедрок (Money, ISBN, DateRange) |
| bounded_context | 1 | сама граница; перечерчивается крайне редко |
### Типовые связи (источник → цель, вниз по шкале)
- **page → use_case / epic** — `Rel.Realizes<…>()` (страница реализует сценарий; если
use-case-документа нет — реализует эпик напрямую).
- **use_case / epic → endpoint** — `Rel.Uses<…>()` (сценарий/эпик использует endpoint-ы).
- **endpoint → handler** — `Rel.Uses<…>()` (контракт опирается на обслуживающий хендлер).
`Rel.Serves` (подпись «обслуживает») семантически закреплён за направлением handler →
endpoint. Так как handler **стабильнее** endpoint-а, в этом проекте мы объявляем связь на
вариативной стороне — `endpoint → handler` через `Rel.Uses` (а обратная подпись
«используется» появляется на странице хендлера автоматически).
""";
public static string Spec_DocQualityChecks = """
## Проверки качества документа (обязательны при каждом касании)
При создании или правке любого документа спеки проверять три пункта — и **озвучивать
замечание**, если что-то не так (не молча):
1. **Структурные связи проставлены.** Документ, зависящий от другого, обязан нести связь
явно (`IHasStructuralLinks`, см. раздел «Структурные связи»). Забыли проставить —
напомнить и доставить.
2. **Проза, а не структурное описание.** Тело документа пишется связной прозой (по
материализации прозы из секций), а не «структурным конспектом»/списком ради списка.
Встретили документ, написанный структурным описанием вместо прозы — по возможности
переписываем в прозу; как минимум — напоминаем.
3. **Нет документов-сирот.** У документа должна быть хотя бы одна связь — исходящая или
входящая (на него кто-то ссылается, либо он ссылается). Встретили полностью
изолированный документ (никто не ссылается и он ни на кого) — напомнить: либо вписать
его в граф, либо это сигнал, что документ лишний/недописан.
""";
public static string Spec_ChangeRequests = """
## Запросы на изменение (Change Request)
Замечания после тестирования и пожелания пользователей оформляются **батчами** в документы
kind `IChangeRequestDocument` (папка «Запросы на изменение», `35_ChangeRequests/`). Цель —
трассируемость: по системе видно, **почему** принято то или иное решение.
**Один батч = один документ.** Имя — дата + тема: `DOC_CR_<гггг>_<мм>_<дд>_<Тема>`. Новые
замечания другой даты — новый документ (старый не правим задним числом, кроме раздела «Итог»).
**Структура CR-документа:**
1. **Источник (как есть)** — дословный текст замечаний/пожеланий (verbatim: язык и опечатки
сохраняем, как при миграции).
2. **Разбор** — что меняем по каждому пункту, детали, открытые вопросы.
3. **Blast radius** — на что влияет: спеки (страницы/компоненты/endpoint/use-case), backend,
UI, миграции/swagger. Затронутые документы объявляются структурными связями
(`Rel.Uses<…>`) — тогда влияние видно на «Графе связей», а на затронутых страницах
появляется обратная ссылка на CR.
4. **План** — шаги (спека → реализация), чек-лист.
5. **Итог** — что фактически сделано (со ссылками) + статус по каждому пункту:
`выполнено` / `отложено` / `в работе`.
**Ссылки в прозе.** Кроме раздела «Источник (как есть)» (он verbatim), в прозе CR на каждую
упоминаемую страницу/компонент/endpoint/use-case/термин ставим **навигационную ссылку**
(`SdaidAnchors.RefTo<…>()` в interpolated-строке, либо markdown-ссылка), чтобы из CR можно
было перейти к затронутому документу. Затронутые документы дополнительно объявляем
структурной связью `Rel.Uses<…>` (blast radius на графе).
**UI-изменения — сначала макет.** Если CR меняет внешний вид страницы, сперва обновляем
(или создаём) её wireframe-мокап, показываем пользователю и получаем «ок», и только затем
идём в реализацию. Не начинаем кодить визуальную часть, не показав, что получится.
**Процесс:** оформили CR из «как есть» → обсудили разбор/blast radius/план → идём по нему
(спека + мокап для UI → согласование → реализация) → фиксируем «Итог» и откладываем CR
(статус). Так батчи замечаний не перемешиваются и каждое решение прослеживается до источника.
""";
public static string Spec_ServicesAndAlgorithms = """
## Сервисы и алгоритмы
В backend бывают абстракции, которые не являются ни хендлерами, ни сущностями: сервисы (например, оркестрация обработки файла, отправка уведомлений) и алгоритмы (rusmarc-парсер, экстрактор полей, расчёт markup-критериев). Их тоже надо специфицировать.
""";
public static string Spec_ServicesLocation = $"""
### Расположение
Сервисы и алгоритмы живут в слое `55_Backend` (см. {nameof(M_S6_FolderStructure)}):
- **Доменный / прикладной сервис** — в `55_Backend/Services/<Area>/<Name>Service.cs`.
- **Сервис импорта** — в `55_Backend/Import/<Name>Service.cs`.
- **Файловое хранилище** — в `55_Backend/Files/<Name>Storage.cs`.
- **Инфраструктурный контракт** (MQ, внешние интеграции — контракт на работу с внешней капабилити) — в `70_Infrastructure/<Capability>/`, по необходимости.
- **Алгоритмы** (чистая трансформация данных без побочных эффектов) — рядом с сервисом, который их использует. Отдельный `Algorithms/`-уровень не вводим, пока их немного.
""";
public static string Spec_WhatInServiceFile = """
### Что в C#-файле сервиса
- **Класс** (НЕ интерфейс) — чтобы методы могли иметь тело. Класс — spec-only, не используется в runtime, не реализует интерфейсы и не содержит реальной логики.
- **XML-doc на классе** — назначение сервиса и архитектурная привязка (кто оркестратор, как сервис связан с state machine).
- **XML-doc на каждом методе** — короткий контракт: что делает (1-3 предложения), что возвращает, что НЕ трогает.
- **Тело каждого метода**:
```csharp
var spec = $@"
КАК выполнить:
1. ...
2. ...
ЧЕГО НЕ ДЕЛАТЬ:
- ...
";
throw new NotImplementedException(spec);
```
- Cтрока `spec` — детальные шаги «КАК» (нумерованным списком), затем «ЧЕГО НЕ ДЕЛАТЬ», инварианты (например, оптимистическая блокировка).
- `throw new NotImplementedException(spec)` — гарантирует, что (а) `spec` не unused (без warning'а), (б) при ошибочном вызове в реальной сборке текст spec попадёт в exception message.
- **Result-классы** для результатов методов — отдельные публичные типы рядом, в том же файле.
""";
public static string Spec_WhyClassNotInterface = """
### Почему класс, а не интерфейс
Конвенция «комментарий говорит ЧТО, тело говорит КАК» — естественная и ценная. Интерфейс не позволяет описать «КАК должен работать метод», только сигнатуру и контракт. Если запихать «КАК» в XML-doc, получается смешение «что» и «как» в одном комментарии — теряется смысловое разделение.
Класс с пустыми телами решает это естественно: XML-doc метода = «что», тело со `spec` = «как». Реализация в src-api — отдельная задача и отдельный класс.
Spec-классы:
- живут в `Ban.Sdaid.Icd.csproj`, не подтягиваются в runtime-solution;
- не реализуют никаких интерфейсов (это забота src-api);
- не имеют конкретной логики — только `spec`-строки и `throw NotImplementedException(spec)`.
""";
public static string Spec_WhenStillInterface = """
### Когда всё-таки interface
Если у спеки нет «как» (только контракт типа маркера или dispatch-точки) — допустим интерфейс. Но когда в спеке есть существенный «КАК», — это сигнал использовать класс.
""";
public static string Spec_ServicesWhatNotToDo = """
### Что НЕ делать
- Не реализовывать тела методов. Это спека, не прототип.
- Не описывать в XML-doc высокоуровневые сценарии — это в use-case или state machine, со ссылкой.
- Не дублировать содержимое state machine в спеке сервиса. Сервис описывает **что делает шаг**, state machine — **когда какой шаг разрешён**.
""";
public static string Spec_ServiceHandlerStateMachine = """
### Связка сервис ↔ хендлер ↔ state machine
Типичная картина для сервисов с состоянием (как `IImportJobServiceSpec`):
- **state machine (md)** — какие статусы есть, какие переходы разрешены, **когда** какой переход.
- **сервис (interface, cs)** — **что конкретно происходит** на конкретном шаге (verify, process batch); сервис не знает о state machine и о конкретном вызывающем — может переиспользоваться.
- **хендлер (cs)** — каноническое описание «что произойдёт при вызове endpoint» (см. [Каноническое описание операции](#каноническое-описание-операции)). Проводит читателя сквозь всю цепочку, интегрирует state machine и сервис в цельный рассказ.
Все три ссылаются друг на друга через `nameof()` / md-путь / `<see cref>`. Хендлер **не дублирует** содержимое state machine и XML-doc сервиса, **но интегрирует** их: где сервис делает «что», state machine задаёт «когда какой переход», а хендлер связывает их в «при вызове endpoint происходит вот это».
""";
public static string Spec_CanonicalOperationDescription = """
## Каноническое описание операции
Когда читатель приходит со вопросом «что произойдёт, если дёрнуть этот endpoint», он должен в **одном месте** получить полную картину — без необходимости стыковать обрывки из 4-х файлов.
Этим местом является **handler-spec** — `string spec` в `<Name>Handler.cs`.
""";
public static string Spec_DescriptionDistribution = """
### Распределение описания
| Файл / артефакт | Где живёт | Что описывает |
|---|---|---|
| **Endpoint (`<Name>Endpoint.cs`)** | `45_Api/Endpoints/<Area>/` | Назначение операции в 1-3 предложения + указатель на handler через `<see cref>`. **Не описывает поля** — XML-doc каждого поля живёт на самом поле в `Query`/`Command`/`Result`. |
| **Handler (`<Name>Handler.cs`)** | `55_Backend/Handlers/<Area>/` | **Каноническая narrative** — проводит сквозь всю цепочку: валидация входа → загрузка данных → ветвление по состоянию → вызовы методов сервиса (с **краткими 1-2-предложными summary**) → применение результата → сохранение → ответ. Реакции на ошибки и не-happy-path тоже здесь. |
| **Service (`<Name>Service.cs`)** | `55_Backend/Services/<Area>/` (или `Import/`, `Files/`) | Что конкретно делает один шаг — без оркестрационного контекста и без ссылок на конкретного вызывающего (сервис может переиспользоваться). Класс, не интерфейс (см. соответствующую секцию). |
| **State machine (`<Aggregate>StateMachine.cs`)** | `60_Epics/<E>/UseCases/<UC>/StateMachines/` | Какие статусы и переходы разрешены, при каких условиях. Не содержит «как работает конкретный handler». Описывается через ISdaidDocumentSection с mermaid в Body. |
| **Use case** | `60_Epics/<E>/UseCases/<UC>/` (сам класс use-case-а как `IUseCaseDocument` + Given/When/Then секциями) | Сквозной сценарий пользователя через **несколько endpoint-ов**. Если операция ограничена одним endpoint — use-case не нужен. |
""";
public static string Spec_CanonicalWhatNotToDo = """
### Что НЕ делать
- **Не делать handler «самым тонким»**. Тонкий handler делает читателя несчастным: full story рассыпается. Handler не дублирует детали сервиса/state-machine, но **интегрирует** их.
- **Не плодить overview-документы**. Если возникает соблазн написать `ProceedImportJob.overview.md` — это плохой признак: содержание должно жить в handler-spec, а не рядом.
- **Не дублировать narrative в endpoint-XML-doc**. Endpoint описывает только назначение (1-3 предложения) и указывает на handler. Полное описание — в handler-spec.
- **Не описывать поля endpoint в endpoint-summary**. Каждое поле имеет своё XML-doc на самом себе — там и хранится описание (тип, default, диапазон, связанные правила). Дубликат в summary endpoint-класса гарантирует drift: при изменении поля никто не вспомнит поправить summary. Если нужна компайл-сейфти ссылка — `<see cref>` на тип поля в endpoint summary, а не пересказ.
- **Не описывать в сервисе оркестрационный контекст**. Сервис рассказывает «что я делаю на одном вызове моего метода», без знания о том, кто и в каком порядке его вызывает. Иначе он перестаёт быть переиспользуемым.
""";
public static string Spec_HandlerSpecLength = """
### Длина handler-spec
- Для query-эндпоинтов с простой логикой — 3-7 шагов, 10-20 строк.
- Для command-эндпоинтов с workflow / state machine — 30-60 строк, и это нормально: handler остаётся **единственным** местом «что произойдёт».
""";
public static string Spec_ConstAliases = """
### Ссылки в spec-строках — inline nameof / RefTo
`nameof(...)` и `SdaidAnchors.RefTo<...>()` ставятся **inline** в spec-строках.
Прежнее требование выносить их в class-level `const`-алиасы **отменено**: спека
читается в сгенерированном HTML (не в исходнике), а алиасы создают лишний шум при
чтении кода (в т.ч. для ИИ-агента). При необходимости подключаем namespace через `using`.
""";
public static string Spec_PageModalForms = """
## Страницы и формы (page / modal / drawer)
Страница описывается **прозой** — какие возможности от неё ожидаются, а не структурой
компонентов. Это `IPageDocument` (в `65_Ui/Pages/<Area>/`) с:
- `Description` — одно предложение: что это за страница;
- один-два прозовых блока (`public static string`), например «Возможности» и
«Карточка (drawer)» — флоу-текст: что показывает список (грид, поля, поиск, подгрузка),
что можно открыть, создать, отредактировать, удалить; какие защиты и какой доступ.
**Ссылки — типизированные** (`SdaidAnchors.RefTo<...>()`): на endpoints-источники данных и
команды, на доменные сущности, на другие страницы. Клик ведёт на их страницы,
переименование ловит компилятор. Голую прозу-имя без ссылки не оставляем.
**Список и его карточка/форма (drawer/modal)** живут в **одном** документе страницы, пока
близки по смыслу. Если форма вырастает в модалку или отдельную страницу редактирования —
выделяется в отдельный `IPageDocument`.
### Что НЕ пишем
- **Структуру компонентов** — никаких именованных свойств-кнопок, блоков колонок,
`dynamic`-полей, таблиц «Name/Required/Sends». Это шум; нужное говорится прозой.
- **Размещение в меню/навигации** — решение sitemap-а (см. ADR-UI routing), не свойство страницы.
- **Точную вёрстку** — это HTML-прототип и реализация, не спека-описание.
### Доступ
Доступ к странице и действиям — через UI-claims (`rftm/ui/menu`, `rftm/ui/entity`);
серверные операции дополнительно проверяются на backend. В прозе указываем ожидаемый доступ.
### HTML-прототип
Кликабельный макет кладётся в `html/` рядом со страницей и встраивается в спеку через
`SdaidHtml.IframeOf<TPage>(...)` (+ `SdaidHtml.LinkTo<TPage>(...)` — открыть в новой вкладке).
Прототип берёт стили из **единого** `65_Ui/html/shared.css` (линк
`../../../html/shared.css`) — **не инлайнит** свой CSS, чтобы прототипы не «плыли» друг от
друга. Разметка прототипа самодостаточна, общий стиль — один на все.
""";
public static string Spec_WhenUseCase = """
## Когда нужен use-case, а когда нет
Use-case-документ создаётся **только** если выполнено хотя бы одно:
- сценарий проходит через 2+ страницы;
- запускает фоновый процесс или интеграцию;
- задевает 2+ bounded context;
- имеет состояния, сохраняемые между шагами;
- содержит cross-cutting NFR.
Иначе — описание живёт в page-спеке. «Просмотр одного грида + одного query» — **не** use-case, это функциональность одной страницы.
Use-case — карта пересечений: цель, актор, шаги Given/When/Then на бизнес-уровне (без слов «контроллер», «компонент»), таблица «шаг → page → endpoint → handler → entity», локальные NFR, открытые вопросы. Пересказ page-спеки в use-case — запрещён.
""";
public static string Spec_StateMachines = """
## State machines
State-machine состоит из трёх частей, и попытка положить всё в один формат теряет одну из них:
1. **Структура** (статусы, допустимые переходы) — таблица.
2. **Поведение** (что делается на переходе) — проза.
3. **Архитектурная привязка** (где исполняется) — короткий блок.
В новой методологии state-machine — это **C#-класс с маркером `IStateMachineDocument`**, в Body секций которого:
- Перечень статусов берётся из enum (`IDomainEntityDocument` на enum-сущности в `50_Domain/<BC>/`) — форма уже зафиксирована в коде.
- Mermaid `stateDiagram-v2` для визуализации (в Body секции).
- Таблица допустимых переходов с триггерами (в Body секции).
- Проза по поведению на каждом переходе (отдельная секция или абзац).
- В конце — где и как исполняется (оркестратор, сервисы).
Файл размещается рядом с use-case-ом, который владеет машиной: `60_Epics/<E>/UseCases/<UC>/StateMachines/<Aggregate>StateMachine.cs`. Имена статусов компайл-сейфтятся через `nameof()` на enum.
""";
public static string Spec_Naming = """
## Именование
- Все классы — **PascalCase**: `ImportJobsListPage.cs`, `CreateImportJobUseCase.cs`.
- Use-case — PascalCase, имя класса говорит о действии: `CreateImportJob`, `VerifyInformationMap`.
- State-machine — PascalCase с суффиксом `StateMachine` (или `Lifecycle` для не-статусных машин): `ImportJobStateMachine`.
- Page artifacts (три ссылки):
- UiSpec (требование): `<Name>UiSpec.cs` — например `ImportJobsListUiSpec`.
- Прототип (HTML): `<Name>.html` рядом со спекой в `80_Subsystems/Ui/Prototypes/`.
- Page implementation: `<Name>Page.cs` (+ опц. `<Name>Page.md`) в `80_Subsystems/Ui/Pages/`.
- Имена не дублируют контекст папки: внутри `InformationMapImport/` пишем `ImportJobsListPage`, а не `InformationMapImportJobsListPage`.
- UI-реализация (Angular) ссылается на спеку, **не** наоборот. Имена файлов в коде Angular могут отличаться от имён файлов спеки.
""";
public static string Spec_AdrVsEpicAdr = """
## ADR vs adr.md эпика
- `40_Arch/ADR/` — сквозные решения проекта со строгой нумерацией: `ADR_<NNN>_<Name>.cs` (класс с `IAdrDocument`). Сюда попадает то, что переиспользуется или влияет на проект целиком.
- `40_Arch/Tech/<Stack>/` — техно-практики (Angular, .NET, EF). Тоже `IAdrDocument`-классы, но привязаны к стеку.
- `60_Epics/<E>/Adr/` — локальные решения этого эпика. Классы вида `R_N_<Name>.cs` (всё ещё `IAdrDocument`, кодировка локальная — нумерация R-1, R-2 в пределах папки).
- `80_Subsystems/<S>/Adr/` — ADR деплой-единицы.
Если решение в эпик-локальном Adr начинает повторяться в других эпиках — поднимаем его в `40_Arch/ADR/` отдельной задачей.
""";
public static string Spec_OpenQuestions = """
## Открытые вопросы
Все нерешённые вопросы эпика живут **в одном месте** — секция «Открытые вопросы» в `IEpicDocument`-классе эпика (`60_Epics/<E>/`-readme). Use-cases, UI-spec-и, handler-spec-и и state-machine-ы на эти вопросы **ссылаются** через `nameof()` на конкретные секции (OQ-секции эпика — каждая отдельный `ISdaidDocumentSection`-класс, тогда auto-linkify работает).
Это запрещает спеке выглядеть «готовой», когда в ней дыры.
""";
public static string Spec_AcceptanceCriteria = """
## Acceptance criteria
AC живут на **двух уровнях**:
- `60_Epics/<E>/Acceptance.cs` (или секции внутри эпик-readme) — **cross-cutting AC всего эпика** (системно-важные).
- `60_Epics/<E>/UseCases/<UC>/Acceptance.cs` — **локальные AC use-case-а**.
Главное представление — **сводный список всех AC по проекту со статусами** (генерится автоматически по kind-index-у или через faceted view).
Правила:
- **Только пользователь-наблюдаемое поведение.** Внутренние инварианты бэка (защита запрещённых переходов state-machine, оптимистические блокировки, конкурентность) описаны в state-machine и реализуются автоматически — **не выносить** в AC.
- **Измеримость.** Каждое AC — потенциальный e2e-тест или ручной чеклист.
- **Префикс по группе** для удобства ссылок: AC-C* (создание), AC-L* (список), AC-D* (детали), AC-S* (lifecycle, наблюдаемая часть), AC-X* (consistency данных), AC-N* (NFR). Состав групп — на усмотрение эпика.
- AC, заблокированные открытыми вопросами, помечаются ссылкой на соответствующий OQ через `nameof()`.
""";
public static string Spec_ImplementationMap = """
## Карта реализации (traceability)
Traceability в новой методологии **автоматическая** — строится генератором из `nameof()`-упоминаний в прозе. Не нужно вручную поддерживать таблицу «Артефакт → Где»; mention-graph её собирает.
Use-case остаётся самостоятельным документом (`IUseCaseDocument`) потому что:
- Given/When/Then narrative нельзя «выжать» из ссылок на artifact-ы;
- локальные NFR и AC use-case-а — их собственные секции;
- открытые вопросы UC ссылаются на конкретные шаги.
На странице эпика и use-case-а генератор автоматически рендерит блок «Связанные артефакты» (все классы, упомянутые через `nameof()`) и «Где используется» (обратные ссылки).
""";
public static string Spec_HowToWorkWithSpec = """## Как работать с спекой""";
public static string Spec_ForDeveloper = """
### Разработчику
- При работе с реализацией в `src-api/<X>` или `src-ui/<X>` — открой соответствующий артефакт спеки в рендеренном сайте; navigation от него к связанным (handler ↔ service ↔ state machine ↔ use-case) делается через mention-graph.
- При работе с эпиком — начинай с `60_Epics/<E>/`-readme (`IEpicDocument`), оттуда — по auto-linkified ссылкам.
- При расхождении spec ↔ impl — фиксируй вопрос явно. Не приводи код к спеке и не правь спеку под код молча.
- Изменил реализацию → проверь спеку соответствующего use-case-а/handler-а/service-а. Не уверен — добавь OQ-секцию в эпике.
""";
public static string Spec_ForAiAssistant = """
### ИИ-ассистенту
- Перед редактированием кода в зоне эпика — загрузи `IEpicDocument`-readme эпика, связанные `IUseCaseDocument`/`IHandlerDocument`/`IDomainServiceDocument`/`IStateMachineDocument`.
- Перед редактированием спеки — проверь существующую реализацию в `src-api/` или `src-ui/`.
- Не проводить тихую синхронизацию spec ↔ impl: при расхождении явно сообщить пользователю, спросить, что — источник правды.
- При работе с HTML-прототипами UI: cs-спека (`IUiSpecDocument`) важнее прототипа; прототип после реализации компонента не синхронизировать автоматически. Подробнее — секция HTML-прототипы.
""";
public static string Spec_BuildingSpec = """
### Сборка спеки
```bash
dotnet build sdaid/Ban.Sdaid.Icd/Ban.Sdaid.Icd.csproj
```
Проект — это сам SDAID-спека на C#. Компилируется как линтер: переименование сущности через F2 правит все её упоминания, find-references показывает зависимости, ссылки на несуществующие классы (через `nameof()`, `RefTo<T>` и т. п.) ломают сборку.
**После любого обновления методологии (и спеки в целом) — обязательно перегенерировать HTML-сайт вызовом `sdaid\build-html.cmd`.** Сборка проверяет компайл-сейфти, рендер — что секции выглядят как ожидалось; одно без другого оставляет рассинхрон.
""";
public static string M_Antipatterns = """
## Общие антипаттерны
Базовый список того, что **не нужно делать** при ведении спеки. Не привязан к структуре папок — антипаттерны процесса/мышления.
- **Не плодить use-case-ы, когда хватает page-спеки.** Если сценарий = «открыл одну страницу, нажал кнопку, увидел результат» — это поведение страницы, не use-case. Use-case оправдан только при cross-page потоке, фоновом процессе, multi-bounded-context взаимодействии или сохранённом состоянии между шагами.
- **Не выносить внутренние инварианты бэка в acceptance.** AC — это **наблюдаемое пользователем** поведение. Защита запрещённых переходов state-machine, оптимистическая блокировка, конкурентность — описаны в state-machine и реализуются автоматически. В AC они дублируют contracts.
- **Не нумеровать локальные решения эпика в общем ADR-индексе.** Локальные R-N эпика — в `Epics/<E>/Adr/`, со своей нумерацией. Если решение начало повторяться в других эпиках — поднимаем в общий `40_Arch/ADR/` отдельной задачей.
- **Не редактировать вручную сгенерированные файлы.** Если такие появятся (rendered HTML, генерируемый код) — правка идёт через источник, не через output. Иначе drift гарантирован.
""";
public static string Spec_Changelog = """
## Журнал изменений методологии
Принятые изменения **самой методологии** — для тех, кто читал её раньше и хочет увидеть, что
поменялось, не перечитывая документ целиком. Новые записи — сверху; дата — когда решение
принято. Это журнал решений по методике (не путать с CHANGELOG нотации
`Ban.Sdaid.Notation`, который описывает API авторской нотации).
### 2026-08-03
- **Заведено.** Методология перенесена в проект ICD как основа заготовки спецификации.
Секция «Историческое (только для миграции)» и обсолетный список «Что НЕ нужно делать»
не переносились — это история конкретного проекта-донора, к ICD отношения не имеет.
""";
}
}