@sberbusiness/triplex-next-mcp-server 0.36.0-beta0 → 1.40.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/bundle/components/AlertContext.md +268 -0
  2. package/bundle/components/Button.md +5 -2
  3. package/bundle/components/ButtonDropdown.md +9 -1
  4. package/bundle/components/ButtonDropdownExtended.md +422 -0
  5. package/bundle/components/ButtonIcon.md +7 -1
  6. package/bundle/components/Caption.md +291 -0
  7. package/bundle/components/CodeText.md +197 -0
  8. package/bundle/components/Col.md +311 -162
  9. package/bundle/components/CollapsibleTree.md +6 -3
  10. package/bundle/components/CollapsibleTreeExtended.md +6 -2
  11. package/bundle/components/Dropdown.md +748 -346
  12. package/bundle/components/LightBox.md +2252 -233
  13. package/bundle/components/List.md +26 -1
  14. package/bundle/components/ListActionItem.md +157 -0
  15. package/bundle/components/ListItem.md +34 -9
  16. package/bundle/components/ListItemSelectable.md +2 -2
  17. package/bundle/components/ListMaster.md +4 -5
  18. package/bundle/components/ListTableItem.md +391 -0
  19. package/bundle/components/Notification.md +21 -21
  20. package/bundle/components/Page.md +1 -0
  21. package/bundle/components/Portal.md +131 -67
  22. package/bundle/components/Row.md +171 -69
  23. package/bundle/components/Text.md +320 -0
  24. package/bundle/components/TextField.md +364 -97
  25. package/bundle/components/Title.md +296 -0
  26. package/bundle/guides/commits.md +118 -24
  27. package/bundle/guides/context.md +1 -1
  28. package/bundle/guides/tests.md +42 -0
  29. package/bundle/manifest.json +89 -14
  30. package/bundle/release-notes/1.37.0.mdx +42 -1
  31. package/bundle/release-notes/1.38.0.mdx +36 -0
  32. package/bundle/release-notes/1.39.0.mdx +66 -0
  33. package/bundle/release-notes/1.40.0.mdx +27 -0
  34. package/bundle/release-notes/1.41.0.mdx +7 -0
  35. package/package.json +1 -1
@@ -0,0 +1,296 @@
1
+ ---
2
+ component: Title
3
+ category: Typography
4
+ related: [Text, Caption, CodeText]
5
+ tokens:
6
+ - --triplex-next-Typography-Primary_Color
7
+ - --triplex-next-Typography-PrimaryInvert_Color
8
+ - --triplex-next-Typography-Complementary_Color
9
+ - --triplex-next-Typography-ComplementaryInvert_Color
10
+ - --triplex-next-Typography-Secondary_Color
11
+ - --triplex-next-Typography-SecondaryInvert_Color
12
+ - --triplex-next-Typography-Tertiary_Color
13
+ - --triplex-next-Typography-TertiaryInvert_Color
14
+ - --triplex-next-Typography-Disabled_Color
15
+ - --triplex-next-Typography-DisabledInvert_Color
16
+ - --triplex-next-Typography-Brand_Color
17
+ - --triplex-next-Typography-BrandInvert_Color
18
+ - --triplex-next-Typography-Info_Color
19
+ - --triplex-next-Typography-InfoInvert_Color
20
+ - --triplex-next-Typography-Success_Color
21
+ - --triplex-next-Typography-SuccessInvert_Color
22
+ - --triplex-next-Typography-Warning_Color
23
+ - --triplex-next-Typography-WarningInvert_Color
24
+ - --triplex-next-Typography-Error_Color
25
+ - --triplex-next-Typography-ErrorInvert_Color
26
+ - --triplex-next-Typography-System_Color
27
+ - --triplex-next-Typography-SystemInvert_Color
28
+ stories: stories/Typography/Title.stories.tsx
29
+ version: "1.0"
30
+ ---
31
+
32
+ # Title
33
+
34
+ ## Назначение
35
+
36
+ Заголовок (типографика). Рендерит семантический heading `<h1>`–`<h3>` (тег по умолчанию совпадает со значением `size`) со шрифтом SBSansDisplay и цветом из палитры `EFontType`. `margin` у заголовков обнулён — отступы задаёт layout потребителя.
37
+
38
+ Используй когда: нужен заголовок страницы, секции, карточки или модального окна.
39
+
40
+ Не используй когда:
41
+ - Нужен обычный текст интерфейса — используй `Text`.
42
+ - Нужна крупная цифра/показатель (32px), не являющаяся заголовком — используй `Caption` с размером `D1`.
43
+
44
+ ---
45
+
46
+ ## Варианты и props
47
+
48
+ ### Обязательные props
49
+
50
+ | Prop | Тип | Описание |
51
+ |---|---|---|
52
+ | `size` | `ETitleSize` | Размер заголовка: `H1` (28/36px), `H2` (24/32px), `H3` (20/28px). В скобках — font-size/line-height. Значения enum — строки `"h1"`/`"h2"`/`"h3"`. |
53
+
54
+ ### Опциональные props
55
+
56
+ | Prop | Тип | По умолчанию | Описание |
57
+ |---|---|---|---|
58
+ | `weight` | `EFontWeightTitle` | `SEMIBOLD` | Начертание: `REGULAR` / `MEDIUM` (500) / `SEMIBOLD` (600) / `BOLD` (700). Каждому соответствует своё семейство SBSansDisplay*. |
59
+ | `type` | `EFontType` | `PRIMARY` | Цвет текста из общей палитры типографики (11 базовых + 11 `*_INVERT`). |
60
+ | `underline` | `boolean` | — | Подчёркивание. Совместим со `strikethrough` (оба сразу — `underline line-through`). |
61
+ | `strikethrough` | `boolean` | — | Зачёркивание. |
62
+ | `tag` | `string` | значение `size` | HTML-тег корневого элемента. По умолчанию равен значению `size` (`"h1"`/`"h2"`/`"h3"`) — визуальный размер и уровень heading связаны. Передай `tag` явно, чтобы развязать их (например, `size={ETitleSize.H1} tag="h2"`). |
63
+
64
+ Компонент типизирован как `TTitleProps<T extends keyof JSX.IntrinsicElements>` и принимает все атрибуты соответствующего HTML-элемента через spread.
65
+
66
+ ### Ограничения
67
+
68
+ - Внешний тип `forwardRef<HTMLElement, TTitleProps<keyof JSX.IntrinsicElements>>` «растворяет» дженерик: TypeScript не сужает атрибуты под конкретный `tag`. Известное ограничение публичного API, не исправлять без обсуждения (breaking).
69
+ - Дефолт `` tag = `${size}` `` — намеренный трюк (значения `ETitleSize` совпадают с именами тегов); при изменении значений enum сломается и дефолтный тег — ещё одна причина не трогать значения.
70
+ - `EFontType.BRAND` дополнительно задаёт `user-select: none` (особенность стилей `Typography.module.less`).
71
+
72
+ ---
73
+
74
+ ## Дизайн-токены
75
+
76
+ Цвета — общие CSS-переменные семейства Typography (см. frontmatter): `--triplex-next-Typography-{Type}_Color` и `--triplex-next-Typography-{Type}Invert_Color`.
77
+
78
+ Размеры шрифта и line-height — фиксированные пиксельные значения в `styles/Title.module.less` (токенов для них нет).
79
+
80
+ ---
81
+
82
+ ## Инварианты
83
+
84
+ - **`forwardRef`** — обязателен, `ref` пробрасывается на корневой heading-элемент.
85
+ - **Значения enum** `ETitleSize` (строки `"h1"`/`"h2"`/`"h3"`), `EFontWeightTitle`, `EFontType` — публичный API из barrel. Изменение — breaking change; значения `ETitleSize` дополнительно используются как дефолтный `tag`.
86
+ - **`TTitleProps`**, **`ITypographyProps`** — публичные типы.
87
+ - **`displayName = "Title"`** — не убирать.
88
+ - `margin: 0` на `h1`–`h3` в `Title.module.less` — контракт: отступы вокруг заголовка задаёт потребитель.
89
+ - Классы собираются только через `clsx`.
90
+
91
+ ---
92
+
93
+ ## Accessibility
94
+
95
+ - По умолчанию рендерится настоящий heading (`<h1>`–`<h3>`) — уровень в accessibility-дереве определяется `size`. Следи за иерархией заголовков страницы; если визуальный размер не совпадает с нужным уровнем — задай `tag` явно.
96
+ - Компонент не интерактивный: focus management и keyboard navigation не применимы.
97
+ - Цвет (`type`) не передаёт семантику для screen reader'ов.
98
+
99
+ ---
100
+
101
+ ## Связанные компоненты
102
+
103
+ - `Text` — основной текст интерфейса (B1–B4, SBSansText).
104
+ - `Caption` — мелкие подписи и крупный показатель D1.
105
+ - `CodeText` — моноширинный текст.
106
+
107
+ ---
108
+
109
+ ## Stories
110
+
111
+ Основные истории: `stories/Typography/Title.stories.tsx`
112
+ Файлы примеров: `stories/Typography/examples/Title/`
113
+
114
+ | Story | Example file | Что демонстрирует |
115
+ |---|---|---|
116
+ | `Playground` | `Playground.tsx` | Интерактивный контроль props `size`, `weight`, `type`, `tag`, `underline`, `strikethrough` |
117
+ | `Default` | `Default.tsx` | Минимальное использование |
118
+ | `Sizes` | `Sizes.tsx` | Размеры H1 / H2 / H3 |
119
+ | `Weights` | `Weights.tsx` | Начертания REGULAR / MEDIUM / SEMIBOLD / BOLD |
120
+ | `Types` | `Types.tsx` | Все значения `EFontType`: базовые + инвертированные на тёмной подложке |
121
+ | `Decorations` | `Decorations.tsx` | `underline`, `strikethrough` и их комбинация |
122
+
123
+ ---
124
+
125
+ ## История изменений
126
+
127
+ | Дата | Изменение |
128
+ |---|---|
129
+ | 2026-07-20 | Создан документ. AI-рефакторинг: decoration-логика вынесена во внутренний хелпер `getTextDecorationClassName` (`utils.ts`), комментарии переведены в JSDoc, unit-тесты расширены (все значения `ETitleSize`/`EFontWeightTitle`, дефолтные heading-теги). Stories переведены на modern pattern (`examples/Title/`). |
130
+
131
+ ---
132
+
133
+ ## Примеры использования
134
+
135
+ ### Default
136
+
137
+ `stories/Typography/examples/Title/Default.tsx`
138
+
139
+ ```tsx
140
+ import React from "react";
141
+ import { Title, ETitleSize } from "@sberbusiness/triplex-next";
142
+
143
+ export const Default = () => <Title size={ETitleSize.H1}>Заголовок страницы</Title>;
144
+ ```
145
+
146
+ ### Sizes
147
+
148
+ `stories/Typography/examples/Title/Sizes.tsx`
149
+
150
+ ```tsx
151
+ import React from "react";
152
+ import { Title, ETitleSize } from "@sberbusiness/triplex-next";
153
+
154
+ export const Sizes = () => (
155
+ <div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
156
+ <Title size={ETitleSize.H1}>Заголовок H1</Title>
157
+ <Title size={ETitleSize.H2}>Заголовок H2</Title>
158
+ <Title size={ETitleSize.H3}>Заголовок H3</Title>
159
+ </div>
160
+ );
161
+ ```
162
+
163
+ ### Weights
164
+
165
+ `stories/Typography/examples/Title/Weights.tsx`
166
+
167
+ ```tsx
168
+ import React from "react";
169
+ import { Title, ETitleSize, EFontWeightTitle } from "@sberbusiness/triplex-next";
170
+
171
+ export const Weights = () => (
172
+ <div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
173
+ <Title size={ETitleSize.H2} weight={EFontWeightTitle.REGULAR}>
174
+ Regular - Обычный вес
175
+ </Title>
176
+ <Title size={ETitleSize.H2} weight={EFontWeightTitle.MEDIUM}>
177
+ Medium - Средний вес
178
+ </Title>
179
+ <Title size={ETitleSize.H2} weight={EFontWeightTitle.SEMIBOLD}>
180
+ Semibold - Полужирный
181
+ </Title>
182
+ <Title size={ETitleSize.H2} weight={EFontWeightTitle.BOLD}>
183
+ Bold - Жирный
184
+ </Title>
185
+ </div>
186
+ );
187
+ ```
188
+
189
+ ### Types
190
+
191
+ `stories/Typography/examples/Title/Types.tsx`
192
+
193
+ ```tsx
194
+ import React from "react";
195
+ import { Title, ETitleSize, EFontType } from "@sberbusiness/triplex-next";
196
+
197
+ export const Types = () => (
198
+ <div style={{ display: "flex", gap: "16px" }}>
199
+ <div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
200
+ <Title size={ETitleSize.H2} type={EFontType.PRIMARY}>
201
+ Primary
202
+ </Title>
203
+ <Title size={ETitleSize.H2} type={EFontType.COMPLEMENTARY}>
204
+ Complementary
205
+ </Title>
206
+ <Title size={ETitleSize.H2} type={EFontType.SECONDARY}>
207
+ Secondary
208
+ </Title>
209
+ <Title size={ETitleSize.H2} type={EFontType.TERTIARY}>
210
+ Tertiary
211
+ </Title>
212
+ <Title size={ETitleSize.H2} type={EFontType.BRAND}>
213
+ Brand
214
+ </Title>
215
+ <Title size={ETitleSize.H2} type={EFontType.INFO}>
216
+ Info
217
+ </Title>
218
+ <Title size={ETitleSize.H2} type={EFontType.SUCCESS}>
219
+ Success
220
+ </Title>
221
+ <Title size={ETitleSize.H2} type={EFontType.WARNING}>
222
+ Warning
223
+ </Title>
224
+ <Title size={ETitleSize.H2} type={EFontType.ERROR}>
225
+ Error
226
+ </Title>
227
+ <Title size={ETitleSize.H2} type={EFontType.DISABLED}>
228
+ Disabled
229
+ </Title>
230
+ <Title size={ETitleSize.H2} type={EFontType.SYSTEM}>
231
+ System
232
+ </Title>
233
+ </div>
234
+ <div style={{ display: "flex", flexDirection: "column", gap: "16px", backgroundColor: "#1F1F22" }}>
235
+ <Title size={ETitleSize.H2} type={EFontType.PRIMARY_INVERT}>
236
+ Primary Invert
237
+ </Title>
238
+ <Title size={ETitleSize.H2} type={EFontType.COMPLEMENTARY_INVERT}>
239
+ Complementary Invert
240
+ </Title>
241
+ <Title size={ETitleSize.H2} type={EFontType.SECONDARY_INVERT}>
242
+ Secondary Invert
243
+ </Title>
244
+ <Title size={ETitleSize.H2} type={EFontType.TERTIARY_INVERT}>
245
+ Tertiary Invert
246
+ </Title>
247
+ <Title size={ETitleSize.H2} type={EFontType.BRAND_INVERT}>
248
+ Brand Invert
249
+ </Title>
250
+ <Title size={ETitleSize.H2} type={EFontType.INFO_INVERT}>
251
+ Info Invert
252
+ </Title>
253
+ <Title size={ETitleSize.H2} type={EFontType.SUCCESS_INVERT}>
254
+ Success Invert
255
+ </Title>
256
+ <Title size={ETitleSize.H2} type={EFontType.WARNING_INVERT}>
257
+ Warning Invert
258
+ </Title>
259
+ <Title size={ETitleSize.H2} type={EFontType.ERROR_INVERT}>
260
+ Error Invert
261
+ </Title>
262
+ <Title size={ETitleSize.H2} type={EFontType.DISABLED_INVERT}>
263
+ Disabled Invert
264
+ </Title>
265
+ <Title size={ETitleSize.H2} type={EFontType.SYSTEM_INVERT}>
266
+ System Invert
267
+ </Title>
268
+ </div>
269
+ </div>
270
+ );
271
+ ```
272
+
273
+ ### Decorations
274
+
275
+ `stories/Typography/examples/Title/Decorations.tsx`
276
+
277
+ ```tsx
278
+ import React from "react";
279
+ import { Title, ETitleSize } from "@sberbusiness/triplex-next";
280
+
281
+ export const Decorations = () => (
282
+ <div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
283
+ <Title size={ETitleSize.H2}>Заголовок без декораций</Title>
284
+ <Title size={ETitleSize.H2} underline>
285
+ Заголовок с подчеркиванием
286
+ </Title>
287
+ <Title size={ETitleSize.H2} strikethrough>
288
+ Заголовок с зачеркиванием
289
+ </Title>
290
+ <Title size={ETitleSize.H2} underline strikethrough>
291
+ Заголовок с подчеркиванием и зачеркиванием
292
+ </Title>
293
+ </div>
294
+ );
295
+ ```
296
+
@@ -2,22 +2,40 @@
2
2
 
3
3
  ---
4
4
 
5
+ ## Задачи и тикеты
6
+
7
+ Задачи ведутся в Linear, команда `TRI` (https://linear.app/triplex-next).
8
+ Номер задачи `TRI-XXX` — обязательный префикс веток и коммитов: по нему
9
+ GitHub-интеграция Linear линкует PR и двигает статусы задачи
10
+ (ветка → In Progress, PR → In Review, merge → Done).
11
+
12
+ **Legacy:** исторические ветки и коммиты используют префикс `TRIPLEX-XXX`.
13
+ Существующие не переименовываем; всё новое — только `TRI-XXX`.
14
+
15
+ ---
16
+
5
17
  ## Именование веток
6
18
 
7
- **Формат:** `TRIPLEX-XXX-короткое-описание`
19
+ **Формат:** `TRI-XXX-короткое-описание`
8
20
 
9
21
  - Строчные буквы, слова через дефис.
10
- - Всегда начинается с номера тикета.
22
+ - Всегда начинается с номера задачи Linear.
11
23
 
12
24
  ```text
13
- TRIPLEX-844-ai-ready-design-system
14
- TRIPLEX-901-add-badge-component
15
- TRIPLEX-912-fix-button-focus-style
25
+ TRI-12-add-badge-component
26
+ TRI-15-fix-button-focus-style
27
+ TRI-21-alert-ai-ready
16
28
  ```
17
29
 
18
30
  **Исключения:**
19
- - Релизная ветка: `prepare-release-XX` (например, `prepare-release-2`)
20
- - Перелитие main → release-0: выполняется мейнтейнерами, формат произвольный
31
+ - Релизная ветка: `prerelease-X.Y.Z` (например, `prerelease-1.40.0`) — в неё
32
+ собираются фиксы релиза и бамп версии, PR идёт в `main`
33
+ - Перелитие main → release-0: выполняется без отдельной ветки, прямым мержем
34
+
35
+ Исключение касается только **имени ветки**. Коммиты внутри неё подчиняются
36
+ общему правилу `TRI-XXX`: на релиз заводится задача Linear, её номер идёт
37
+ префиксом (`TRI-XXX Подготовка релиза 1.40.0`). Legacy-префикс `TRIPLEX-0`
38
+ для новых релизов не используется.
21
39
 
22
40
  ---
23
41
 
@@ -28,29 +46,34 @@ TRIPLEX-912-fix-button-focus-style
28
46
  | `main` | React 18 | `1.xx` | Основная ветка разработки |
29
47
  | `release-0` | React 17 | `0.xx` | Поддержка React 17, синхронизируется с main |
30
48
 
31
- **Правило:** все фича-ветки создаются от `main`. Изменения попадают в `release-0` через перелитие — вручную мейнтейнерами. Агент не должен создавать ветки от `release-0` и не должен мерджить в неё напрямую.
49
+ **Правило:** все фича-ветки создаются от `main`. Изменения попадают в `release-0` через перелитие. Агент не должен создавать ветки от `release-0` и не должен мерджить в неё напрямую.
50
+
51
+ **Единственное исключение** — skill `release-react17` в рамках выпуска релиза:
52
+ он мержит `main` в `release-0` и пушит напрямую, без PR. Там это компенсируется
53
+ обязательными локальными гейтами (`tsc`, `test-unit`, `build`) и показом diff
54
+ разработчику — `ci.yml` триггерится только на `pull_request` и прямой push
55
+ не проверяет. Вне релиза перелитие остаётся за мейнтейнерами.
32
56
 
33
57
  ---
34
58
 
35
59
  ## Формат коммитов
36
60
 
37
- **Формат:** `TRIPLEX-XXX Краткое описание`
61
+ **Формат:** `TRI-XXX Краткое описание`
38
62
 
39
63
  ```text
40
- TRIPLEX-844 Добавить AI-Ready документацию для компонентов
41
- TRIPLEX-901 Добавить компонент Badge с темами и размерами
42
- TRIPLEX-912 Исправить стиль фокуса для Button в теме Link
64
+ TRI-12 Добавить компонент Badge с темами и размерами
65
+ TRI-15 Исправить стиль фокуса для Button в теме Link
43
66
  ```
44
67
 
45
68
  Если нужно уточнение — добавляй через двоеточие:
46
69
 
47
70
  ```text
48
- TRIPLEX-844 AI-Ready Design System: фундамент и пилот Button
49
- TRIPLEX-901 Badge: добавить тему DANGER и размер SM
71
+ TRI-12 Badge: добавить тему DANGER и размер SM
72
+ TRI-21 Alert: AI refactoring + unit-тесты
50
73
  ```
51
74
 
52
75
  **Правила:**
53
- - Все коммиты в ветке начинаются с одного номера тикета.
76
+ - Все коммиты в ветке начинаются с одного номера задачи.
54
77
  - Первая строка — не длиннее 72 символов.
55
78
  - Тело коммита опционально (для сложных изменений).
56
79
  - Язык: русский или английский — не смешивать в одном коммите.
@@ -99,30 +122,101 @@ npx prettier --write src/components/Button/Button.tsx
99
122
 
100
123
  ## Когда коммитить и пушить
101
124
 
102
- **Агент не создаёт коммиты самостоятельно.** После завершения задачи — остановиться и показать результат разработчику. Коммит делается только по явной просьбе.
103
-
104
- **Агент не пушит ветку самостоятельно.** Пуш — только по явной просьбе разработчика.
125
+ **Вне задачи Linear агент не создаёт коммиты и не пушит самостоятельно.**
126
+ После завершения работы — остановиться и показать результат разработчику.
127
+ Коммит и пуш — только по явной просьбе.
128
+
129
+ **Исключение — работа по задаче Linear.** Если работа ведётся в ветке задачи
130
+ `TRI-XXX-...` (взятой через skill `take-task`), агент после завершения работ
131
+ и зелёного ревью (`change-reviewer`) **без подтверждения**:
132
+
133
+ 1. делает коммит по конвенциям этого файла (skill `commit-component`);
134
+ 2. пушит ветку;
135
+ 3. создаёт PR (см. «PR-воркфлоу»);
136
+ 4. если менялись stories или визуал компонентов — обновляет baseline-скриншоты
137
+ через skill `update-visual-baselines` (CI-workflow «Update Visual
138
+ Snapshots» + чистка orphan-скриншотов);
139
+ 5. запускает skill `finish-task` — комментарий-резюме публикуется в задачу
140
+ без подтверждения, проверяется линковка PR;
141
+ 6. запускает агента `pr-reviewer` — независимое ревью PR «с чистого листа»
142
+ с комментариями в GitHub (только COMMENT, вердикт и мерж за человеком).
143
+
144
+ Гейты остаются обязательными и в автоматическом режиме — при любом из них
145
+ остановиться и спросить разработчика:
146
+
147
+ - упавшие проверки (tsc / vitest / eslint);
148
+ - изменение публичного поведения без записи в release notes;
149
+ - блокеры от `change-reviewer`;
150
+ - подозрительные/посторонние файлы в `git status`.
105
151
 
106
152
  ---
107
153
 
108
- ## PR-воркфлоу
154
+ ## GitHub-операции от аккаунта бота
155
+
156
+ Автоматические GitHub-операции (создание PR, комментарии в PR, запуск
157
+ workflow, авторство коммитов автофинала) выполняются от отдельного
158
+ machine-аккаунта, если у разработчика задан его токен:
159
+
160
+ ```bash
161
+ # fine-grained PAT бота: contents:write, pull-requests:write, actions:write
162
+ export TRIPLEX_BOT_GH_TOKEN=... # в ~/.zshenv (не ~/.zshrc — агент работает
163
+ # в неинтерактивном shell), НЕ в репозитории
164
+ ```
165
+
166
+ **Паттерн для агентов** — в начале Bash-вызова с командами `gh`:
167
+
168
+ ```bash
169
+ if [ -n "$TRIPLEX_BOT_GH_TOKEN" ]; then export GH_TOKEN="$TRIPLEX_BOT_GH_TOKEN"; fi
170
+ gh pr create ... # выполнится от бота; без токена — от разработчика (fallback)
171
+ ```
109
172
 
110
- **Агент пушит ветку — разработчик создаёт PR.**
173
+ **Авторство коммитов автофинала** (identity бота выводится из токена,
174
+ дополнительные переменные не нужны):
111
175
 
112
176
  ```bash
113
- git push -u origin TRIPLEX-XXX-feature-name
177
+ if [ -n "$TRIPLEX_BOT_GH_TOKEN" ]; then
178
+ BOT_LOGIN=$(GH_TOKEN="$TRIPLEX_BOT_GH_TOKEN" gh api user -q .login)
179
+ BOT_EMAIL=$(GH_TOKEN="$TRIPLEX_BOT_GH_TOKEN" gh api user -q '"\(.id)+\(.login)@users.noreply.github.com"')
180
+ git -c user.name="$BOT_LOGIN" -c user.email="$BOT_EMAIL" commit -m "..."
181
+ else
182
+ git commit -m "..."
183
+ fi
114
184
  ```
115
185
 
116
- После пуша разработчик сам открывает PR через GitHub UI или `gh pr create`.
186
+ Правила:
187
+
188
+ - Токен нигде не печатать, не логировать и не коммитить; в репозитории
189
+ он не хранится ни в каком виде.
190
+ - `git push` остаётся по SSH разработчика — авторство коммита и аккаунт
191
+ пушащего могут различаться, это нормально.
192
+ - Проверка «кто я» перед автофиналом: `gh api user -q .login`
193
+ (с учётом паттерна выше) — чтобы в отчёте было видно, от чьего имени
194
+ созданы PR/комментарии.
195
+ - Действия в Linear выполняются от сессии разработчика — бот на них
196
+ не распространяется.
117
197
 
118
- **Исключение:** если разработчик явно просит агента создать PR, используй:
198
+ ---
199
+
200
+ ## PR-воркфлоу
201
+
202
+ **Работа по задаче Linear:** после коммита агент сам пушит ветку и создаёт PR:
119
203
 
120
204
  ```bash
205
+ git push -u origin TRI-XXX-feature-name
121
206
  gh pr create \
122
- --title "TRIPLEX-XXX Краткое описание" \
207
+ --title "TRI-XXX Краткое описание" \
123
208
  --body "## Что сделано\n- ...\n\n## Как проверить\n- ..."
124
209
  ```
125
210
 
211
+ После создания PR финал продолжается автоматически: skill
212
+ `update-visual-baselines` (если менялись stories/визуал) и skill
213
+ `finish-task` — комментарий-резюме в задаче Linear и проверка линковки PR
214
+ (GitHub-интеграция Linear подхватит PR по `TRI-XXX` в имени ветки).
215
+
216
+ **Вне задачи Linear:** агент пушит ветку и создаёт PR только по явной просьбе
217
+ разработчика; по умолчанию PR открывает разработчик через GitHub UI или
218
+ `gh pr create`.
219
+
126
220
  ---
127
221
 
128
222
  ## Release notes
@@ -267,7 +267,7 @@ AI-агента.
267
267
 
268
268
  | Создаём AI.md | Описываем в родителе |
269
269
  |---|---|
270
- | `List`, `ListItem`, `ListItemTable`, `ListItemSelectable`, `ListItemControlsButton`, `ListItemControlsButtonDropdown`, `ListSortable`, `ListSortableItem` | `ListItemControls`, `ListItemContent`, `ListEmptyState`, `ListItemLoading`, `ListItemTailLeft`, `ListItemTailRight`, `ListSortableItemTarget`, `ListSortableItemControls` |
270
+ | `List`, `ListItem`, `ListActionItem`, `ListTableItem`, `ListItemSelectable`, `ListItemControlsButton`, `ListItemControlsButtonDropdown`, `ListSortable`, `ListSortableItem` | `ListItemControls`, `ListItemContent`, `ListEmptyState`, `ListItemLoading`, `ListItemTailLeft`, `ListItemTailRight`, `ListSortableItemTarget`, `ListSortableItemControls` |
271
271
 
272
272
  В ROADMAP-таблице каждый AI.md-достойный компонент идёт отдельной строкой —
273
273
  так считается прогресс покрытия и видны субкомпоненты с собственным API.
@@ -38,6 +38,48 @@ src/components/{ComponentName}/__tests__/{ComponentName}.test.tsx
38
38
 
39
39
  Один файл на компонент. Если компонент имеет субкомпоненты (ButtonIcon, ButtonDropdown), тесты субкомпонентов — в отдельных файлах той же локальной папки тестов.
40
40
 
41
+ ### Импорты `vitest` — только в `*.test.ts` / `*.test.tsx` ⚠️
42
+
43
+ **Правило:** `import ... from "vitest"` (а также `@testing-library/*`, `storybook/test`)
44
+ допустим **исключительно** в файлах с постфиксом `.test.ts` / `.test.tsx`,
45
+ в `vitest.setup.ts` и в `test-utils/`. Нигде больше.
46
+
47
+ Причина — сборка. `vite.config.ts` строит entry-точку из **каждого** файла
48
+ `src/**/*.{ts,tsx}`, кроме исключённых глобом. Любой файл внутри `src/`, который
49
+ импортирует `vitest` и не подпадает под исключения, попадает в бандл, а вместе с
50
+ ним vitest уезжает в общий чанк `vendor` — тот самый, который тянут почти все
51
+ компоненты. Потребитель падает на старте:
52
+
53
+ ```text
54
+ Uncaught Error: Vitest failed to access its internal state.
55
+ ```
56
+
57
+ Именно это произошло в 1.39.0 из-за файла `src/components/Carousel/__tests__/utils.ts`
58
+ (хелпер без постфикса `.test`). Чанк `vendor` вырос с 640 KB до 1.28 MB.
59
+
60
+ **Общие тестовые хелперы** (моки `ResizeObserver`, фейковые `Touch`, подмена
61
+ размеров элементов и т.п.) кладём в `test-utils/` в корне репозитория — вне `src/`,
62
+ поэтому сборка их физически не видит:
63
+
64
+ ```typescript
65
+ // ✅ src/components/Carousel/__tests__/CarouselRoot.test.tsx
66
+ import { getResizeCallback, mockElementSize } from "../../../../test-utils/dom";
67
+
68
+ // ❌ src/components/Carousel/__tests__/utils.ts — станет entry-точкой сборки
69
+ import { vi } from "vitest";
70
+ ```
71
+
72
+ Не называй хелпер `utils.ts` рядом с `utils.test.tsx`: `__tests__/utils.test.tsx` —
73
+ это **тесты для `../utils.ts`** компонента, а не тесты хелпера. Хелперы нельзя
74
+ класть и в сам `*.test.tsx`-файл: при импорте из другого теста его `describe`
75
+ зарегистрируется повторно и тесты прогонятся несколько раз.
76
+
77
+ Страховка на уровне сборки: `npm run build` запускает `scripts/checkBundleSize.js`
78
+ и падает, если в `dist` превышен один из двух лимитов — **700 KB на отдельный
79
+ JS-файл** (ловит распухший `vendor`: всё из `node_modules` сливается в него
80
+ единственным чанком) или **2000 KB суммарно по всем JS-файлам** (ловит утечку
81
+ кода мимо `node_modules` и медленный рост множества мелких файлов).
82
+
41
83
  ### Импорты
42
84
 
43
85
  ```typescript