| 1 | # ADR 0173: Intent card — структурированная фиксация решений в session graph |
| 2 | |
| 3 | **Статус:** Proposed |
| 4 | **Дата:** 2026-07-12 |
| 5 | |
| 6 | ## Резюме |
| 7 | |
| 8 | В agentic-сессии большинство решений — **не ADR**: «почему так в этом ходе», «почему не вариант B», «как проверим». Эта информация **ищется** ежедневно (code review, debug, steer), но **почти не записывается** в VCS — см. эмпирику rationale (Al Safwan & Servant, ESEC/FSE 2019; Tao et al., FSE 2012). |
| 9 | |
| 10 | **Intent card** — мелкий **сессионный** артефакт: типизированное событие в append-only log [0045](0045-agent-chat-persistence-event-log-and-projections.md), проекция в Intercom timeline, материализация в harness [0166](0166-agent-centric-harness-model-comfort-and-pay-per-token-economics.md). Не заменяет ADR; может **питать** черновик ADR при checkpoint. |
| 11 | |
| 12 | Схема v1: **outcome** (что) · **trigger** (боль/зачем сейчас) · **considered[]** (отвергнутые варианты) · **chosen_approach** + **selection_rationale** (что выбрали и почему) · **constraints** · **validation_plan**. |
| 13 | |
| 14 | IOP [0121](0121-intent-oriented-programming-paradigm.md): capture **в потоке** workline, без обязательной формы на 15 полей и без Confluence. |
| 15 | |
| 16 | --- |
| 17 | |
| 18 | ## Связанные ADR |
| 19 | |
| 20 | | ADR | Роль | |
| 21 | |-----|------| |
| 22 | | [0045](0045-agent-chat-persistence-event-log-and-projections.md) | Канон хранения: новый тип события `intent_card_recorded` | |
| 23 | | [0072](0072-chat-topic-cards-intent-melody-keyboard-contract.md) | Topic card = контейнер темы; intent card = решение внутри workline | |
| 24 | | [0096](0096-intercom-topic-card-summary-and-product-spine.md) | Сводка workline может ссылаться на последнюю intent card | |
| 25 | | [0116](0116-intercom-session-tree-and-agent-message-steering.md) | Steer/fork — триггеры предложения карточки | |
| 26 | | [0121](0121-intent-oriented-programming-paradigm.md) | Парадигма IOP; дисциплина намерения | |
| 27 | | [0155](0155-documentation-code-correspondence-and-architectural-drift.md) | L4 (event log) → L0 (ADR); сборка invariant из карточек | |
| 28 | | [0166](0166-agent-centric-harness-model-comfort-and-pay-per-token-economics.md) | Сжатая материализация карточки в system one-liner | |
| 29 | | [0172](0172-conversation-first-habitat.md) | Workline, scope strip, spin-off; habitat для карточек | |
| 30 | | [0174](0174-sedm-software-engineering-decision-making-ux-spine.md) | SEDM: фазы, context card (T2), UX spine; intent card = T1 | |
| 31 | |
| 32 | ### Вне ADR |
| 33 | |
| 34 | | Документ | Роль | |
| 35 | |----------|------| |
| 36 | | [iop-manifest-v1.md](../iop-manifest-v1.md) | IOP: явные намерения vs хаос потока | |
| 37 | | KB `kb-cide-sdm-decision-making-research-v1` (agent-notes) | SDM / information needs → обоснование схемы | |
| 38 | |
| 39 | --- |
| 40 | |
| 41 | ## Контекст |
| 42 | |
| 43 | ### Проблема индустрии |
| 44 | |
| 45 | | Симптом | Источник | |
| 46 | |---------|----------| |
| 47 | | «Зачем это изменение?» — самый важный и часто самый болезненный вопрос | Tao I-1 (парадокс: easy только при хорошем commit message) | |
| 48 | | «Почему не по-другому?» — доминирует в review threads | Pascarella N1 (alternatives) | |
| 49 | | Alternatives, constraints, side effects **ищут**, но **не записывают** | Al Safwan RQ4 (record gap) | |
| 50 | | Ревьюер прыгает issue tracker ↔ diff ↔ CI ↔ chat | CRDM 2025 (tool backpack) | |
| 51 | |
| 52 | Session graph habitat [0172](0172-conversation-first-habitat.md) даёт **worklines и scope**, но без типизированной фиксации решений оператор и агент **повторяют archaeology** при возврате в parked workline или при checkpoint. |
| 53 | |
| 54 | ### Чем intent card не является |
| 55 | |
| 56 | | Артефакт | Отличие | |
| 57 | |----------|---------| |
| 58 | | **ADR** | Долгоживущий, для команды/продукта, `docs/adr/`, invariant | |
| 59 | | **Topic / workline card** | Индекс линии работы; контейнер, не схема решения | |
| 60 | | **Commit message** | Описывает diff, часто post-hoc | |
| 61 | | **Clarification batch** [0031](0031-agent-chat-clarification-batches-and-threading.md) | Ответы на пакет вопросов агента; другой lifecycle | |
| 62 | | **Intent tag** на workline [0172 §12](0172-conversation-first-habitat.md#adr0172-materialization) | Короткий route-hint для MCP; не полный rationale | |
| 63 | |
| 64 | ### Параллель с User Story / Job Story |
| 65 | |
| 66 | Intent card — **гибрид** product story и decision record: |
| 67 | |
| 68 | | Поле карточки | Аналог | |
| 69 | |---------------|--------| |
| 70 | | `trigger` | Job Story: *When [situation]…* / боль | |
| 71 | | `outcome` | *…so I can [outcome]* / observable result | |
| 72 | | `validation_plan` | Acceptance criteria | |
| 73 | | `considered[]` + `selection_rationale` | **Не** в классической US (там solution в story — анти-паттерн); здесь **обязательно** — закрывает N1 и record gap | |
| 74 | |
| 75 | В agentic-цикле человек **выбирает путь**, а не только формулирует backlog item — карточка фиксирует **Process**-фазу SDM (см. KB SDM). |
| 76 | |
| 77 | --- |
| 78 | |
| 79 | ## Решение |
| 80 | |
| 81 | ### 1. Единица и носитель |
| 82 | |
| 83 | 1. **Intent card** — immutable payload, привязанный к `workline_id` и опционально к `message_id` (сообщение, после которого зафиксировано решение). |
| 84 | 2. Канон — событие **`intent_card_recorded`** в `*.events.ndjson` [0045](0045-agent-chat-persistence-event-log-and-projections.md), `schema_version` в payload. |
| 85 | 3. UI — **system card** в timeline активной workline (свёрнута по умолчанию после checkpoint); не отдельная панель. |
| 86 | 4. Редактирование — только **компенсирующее событие** (аналог `message_edited`), не rewrite истории. |
| 87 | |
| 88 | ### 2. Схема payload v1 |
| 89 | |
| 90 | ```json |
| 91 | { |
| 92 | "type": "intent_card_recorded", |
| 93 | "schema_version": 1, |
| 94 | "workline_id": "wl-…", |
| 95 | "message_id": "msg-…", |
| 96 | "card": { |
| 97 | "outcome": "В scope strip видны все open worklines без скролла всей сессии", |
| 98 | "trigger": "При 2+ линиях оператор забывает хвост в parked workline", |
| 99 | "chosen_approach": "Проекция open worklines из session meta", |
| 100 | "selection_rationale": "New Chat на тему рвёт session graph и head-per-workline (moat 0172); meta сохраняет один composer", |
| 101 | "constraints": "Без модалки «создай топик» до первого сообщения [0172 §9]", |
| 102 | "validation_plan": "2 worklines → switch → head сохранён → strip показывает обе" |
| 103 | }, |
| 104 | "considered": [ |
| 105 | { |
| 106 | "approach": "New Chat per topic", |
| 107 | "rejected_because": "плоский чат, нет workline/head" |
| 108 | }, |
| 109 | { |
| 110 | "approach": "Один длинный тред без индекса", |
| 111 | "rejected_because": "скролл, та же потеря контекста" |
| 112 | } |
| 113 | ] |
| 114 | } |
| 115 | ``` |
| 116 | |
| 117 | #### Семантика полей |
| 118 | |
| 119 | | Поле | Смысл | Не путать с | |
| 120 | |------|--------|-------------| |
| 121 | | `outcome` | **Что** должно стать правдой (результат) | «зачем» — это `trigger` | |
| 122 | | `trigger` | **Боль / ситуация / зачем сейчас** | goal в смысле Al Safwan = outcome | |
| 123 | | `chosen_approach` | Короткая метка выбранного пути | не заменяет обоснование | |
| 124 | | `selection_rationale` | **Почему этот путь, а не considered** | обязательна при нетривиальном выборе | |
| 125 | | `constraints` | Ограничения (ADR, время, «не трогать X») | | |
| 126 | | `validation_plan` | Как проверим (smoke, test, checkpoint) | Perform SDM | |
| 127 | | `considered[]` | Отвергнутые варианты + `rejected_because` | tier A, не «потом в tier B» | |
| 128 | |
| 129 | **Tier B (опционально, `schema_version` ≥ 2 или nested):** `side_effects[]`, `dependencies[]`, ссылки `kb_path`, `adr_ref`. |
| 130 | |
| 131 | ### 3. Правила полноты (валидация) |
| 132 | |
| 133 | Карточка **неполная** (агент должен дособрать или запросить одну реплику), если: |
| 134 | |
| 135 | 1. Есть `chosen_approach`, но нет `selection_rationale`. |
| 136 | 2. Нет ни одного элемента в `considered` (кроме явно тривиального «status quo / ничего не менять»). |
| 137 | 3. `outcome` или `trigger` пусты. |
| 138 | |
| 139 | Продукт **не блокирует** сохранение черновика; scope strip / export помечает **incomplete**. |
| 140 | |
| 141 | ### 4. Кто и когда создаёт |
| 142 | |
| 143 | | Инициатор | Сценарий | |
| 144 | |-----------|----------| |
| 145 | | **Агент** | После steer, предложения spin-off [0172 §11](0172-conversation-first-habitat.md#adr0172-spinoff), fork, смены scope — «зафиксирую intent?» | |
| 146 | | **Оператор** | Slash / chord «intent capture» на активной workline | |
| 147 | | **Checkpoint** | Перед export / закрытием workline — prompt «5 полей за 30 сек», если карточки не было | |
| 148 | |
| 149 | Согласие: одна короткая реплика («да» / правка одного поля), не модалка на 15 полей. |
| 150 | |
| 151 | ### 5. UI (целевое) |
| 152 | |
| 153 | ```text |
| 154 | ┌─ Intent · G2 scope strip ─────────────────────────┐ |
| 155 | │ Trigger: теряю хвост при switch │ |
| 156 | │ Outcome: strip показывает open worklines │ |
| 157 | │ Rejected: New Chat — рвёт session graph │ |
| 158 | │ Chosen: meta-проекция — head per workline │ |
| 159 | │ Check: 2 worklines, switch, head OK │ |
| 160 | │ [edit] [→ KB] [draft ADR] │ |
| 161 | └───────────────────────────────────────────────────┘ |
| 162 | ``` |
| 163 | |
| 164 | - **Perceive (SDM):** collapsed в ленте; active card в scope strip one-liner. |
| 165 | - **Process:** раскрыть `considered[]`. |
| 166 | - **Perform:** `validation_plan` → ссылка на verify ladder [0148](0148-agent-execution-environment-verification-ladder-and-native-tooling.md). |
| 167 | |
| 168 | Кнопки эскалации: |
| 169 | |
| 170 | | Действие | Куда | |
| 171 | |----------|------| |
| 172 | | **→ KB** | Scratch / handoff (agent-notes MCP) | |
| 173 | | **draft ADR** | Шаблон: triggers → Context, considered+chosen → Options/Decision, validation → Consequences; **человек accept** — не автосоздание ADR | |
| 174 | |
| 175 | ### 6. Materialization для harness |
| 176 | |
| 177 | Оркестратор [0166](0166-agent-centric-harness-model-comfort-and-pay-per-token-economics.md) может включать в system one-liner **сжатую** последнюю intent card активной workline (outcome + chosen, ≤ N токенов), не полный dump. Соседние worklines — только через scope strip («2 open»), не все карточки в `messages[]`. |
| 178 | |
| 179 | ### 7. Сборка ADR из карточек (направление) |
| 180 | |
| 181 | ```text |
| 182 | N intent cards в workline / по intent tag |
| 183 | → checkpoint: кластер по feature |
| 184 | → draft ADR (Context · Decision · Consequences) |
| 185 | → оператор: Accept / правка / отклонить |
| 186 | → ADR в docs/adr/ + correspondence [0155](0155-documentation-code-correspondence-and-architectural-drift.md) |
| 187 | ``` |
| 188 | |
| 189 | Intent cards остаются в session export как **provenance**; ADR не дублирует их дословно. |
| 190 | |
| 191 | ### 8. Decision history (агент) — addendum v1.1 |
| 192 | |
| 193 | См. unified model §12 (KB `kb-cide-sedm-unified-model-v1`): после исследования агент пишет **`decision_recorded`** — те же поля `card` + `considered[]`, плюс **`findings[]`** (сжатые находки) и **`basis`** (`revision`, `touched_paths`). |
| 194 | |
| 195 | | Событие | Кто | Зачем | |
| 196 | |---------|-----|--------| |
| 197 | | `intent_card_recorded` | оператор | намерение / выбор пути | |
| 198 | | `decision_recorded` | агент | след «находки → решение»; не grep снова при возврате | |
| 199 | | `decision_marked_stale` | система / оператор | код ушёл (другая workline, commit) | |
| 200 | | `decision_superseded` | любой | замена записи (аналог Superseded в ADR) | |
| 201 | |
| 202 | **Cross-workline:** workline A parked, B меняет `touched_paths` из basis A → decisions A → **stale**; при activate A — re-verify, не слепой trust. Materialization: только **active** + one-liner по **stale**. |
| 203 | |
| 204 | --- |
| 205 | |
| 206 | ## Отклонённые альтернативы |
| 207 | |
| 208 | | Альтернатива | Почему отклонена | |
| 209 | |--------------|------------------| |
| 210 | | Только длиннее commit message | Не закрывает alternatives; post-hoc; не в потоке steer | |
| 211 | | 15 полей Al Safwan в обязательной форме | Record gap показывает: не заполняют; satisficing ломается | |
| 212 | | Одно поле `selected_alternative` без `considered` | Воспроизводит record gap; не отвечает на N1 | |
| 213 | | Отдельный ADR на каждую intent card | Bloat; карточка — атом сессии, ADR — кристалл invariant | |
| 214 | | Хранить только в KB, не в event log | Теряется связь workline ↔ решение ↔ message; ломает L4 [0155](0155-documentation-code-correspondence-and-architectural-drift.md) | |
| 215 | |
| 216 | --- |
| 217 | |
| 218 | ## Последствия |
| 219 | |
| 220 | - Расширение типов событий [0045](0045-agent-chat-persistence-event-log-and-projections.md) и проекций Intercom feed. |
| 221 | - Промпты агента / slash: когда предлагать карточку; валидация полноты. |
| 222 | - Export readable: секция «Decisions» из intent cards workline. |
| 223 | - Тесты: round-trip event → projection → harness one-liner. |
| 224 | |
| 225 | --- |
| 226 | |
| 227 | ## Фазы внедрения |
| 228 | |
| 229 | | Фаза | Содержание | |
| 230 | |------|------------| |
| 231 | | **P0** | ADR + схема payload v1 (этот документ) | |
| 232 | | **P1** | `intent_card_recorded` + `decision_recorded` в log + system card в timeline | |
| 233 | | **P2** | Агент propose + operator approve; scope strip one-liner | |
| 234 | | **P3** | → KB, draft ADR template, incomplete marker | |
| 235 | |
| 236 | **Приоритет:** P1–P2 вместе с scope strip [0172 G2](0172-conversation-first-habitat.md#adr0172-phases) — один смысл «решения на экране». |
| 237 | |
| 238 | --- |
| 239 | |
| 240 | ## Открытые вопросы |
| 241 | |
| 242 | - Имя slash-команды и `command_id` (кандидат: `/intercom intent record`). |
| 243 | - Версионирование: одна active card на workline или история карточек (рекомендация: **история** в log, **последняя** в scope strip). |
| 244 | - Связь с `ClarificationBatch`: карточка после закрытия пакета vs отдельное событие. |
| 245 | - Локализация UI-лейблов (Trigger/Outcome) vs wire на EN. |
| 246 | |
| 247 | --- |
| 248 | |
| 249 | ## Punchline |
| 250 | |
| 251 | > **Intent card** — User Story + Decision Record для session graph: **outcome, trigger, considered, chosen+rationale, check** — в event log, не в голове автора. **ADR** собирается из кластера карточек, когда решение стало invariant. |
| 252 | |