⟨/⟩ 00_Methodology/Methodology.cs

741 строк · в начало

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 отношения не имеет.
            """;

    }
}