@rt-tools/agent-kit 0.3.0 → 0.5.0
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.
- package/README.md +194 -30
- package/assets/agents/business-analyst.md +74 -0
- package/assets/agents/project-manager.md +70 -0
- package/assets/agents/qa-engineer.md +72 -0
- package/assets/agents/skill-curator.md +110 -0
- package/assets/agents/spec-critic.md +44 -0
- package/assets/agents/spec-writer.md +50 -0
- package/assets/checks/board.github.mjs +329 -0
- package/assets/checks/check-board.github.mjs +181 -0
- package/assets/checks/check-doc-paths.mjs +163 -0
- package/assets/checks/check-dupes.mjs +277 -0
- package/assets/checks/check-lib-layers.mjs +573 -0
- package/assets/checks/check-reuse.mjs +208 -0
- package/assets/checks/check-schema-drift.mjs +186 -0
- package/assets/checks/check-specs.mjs +1086 -0
- package/assets/checks/check-styles.mjs +109 -0
- package/assets/checks/rt-kit-checks.config.mjs +134 -0
- package/assets/checks/task-new.github.mjs +198 -0
- package/assets/commands/skill-curator.md +70 -0
- package/assets/defaults/gate-map.sh +106 -0
- package/assets/defaults/project.sh +204 -0
- package/assets/hooks/browser-device-id.sh +0 -0
- package/assets/hooks/browser-guard-device-id.sh +2 -1
- package/assets/hooks/browser-guard-no-asking.sh +27 -0
- package/assets/hooks/browser-guard-no-listing.sh +2 -1
- package/assets/hooks/browser-guard-no-other-drivers.sh +2 -1
- package/assets/hooks/browser-guard-require-select.sh +2 -1
- package/assets/hooks/commit-msg.sh +1 -1
- package/assets/hooks/constitution-index.sh +5 -4
- package/assets/hooks/dev-server-guard.sh +8 -6
- package/assets/hooks/docs-guard.sh +223 -37
- package/assets/hooks/git-guard-delivery.sh +171 -31
- package/assets/hooks/git-guard-main.sh +1 -0
- package/assets/hooks/git-guard-push-tests.sh +34 -13
- package/assets/hooks/glossary-load.sh +23 -0
- package/assets/hooks/grill-gate.sh +96 -0
- package/assets/hooks/lint-after-edit.sh +155 -30
- package/assets/hooks/qa-dataid-guard.sh +72 -32
- package/assets/hooks/reuse-first-guard.sh +105 -34
- package/assets/hooks/skill-gate-rearm.sh +1 -0
- package/assets/hooks/skill-gate.sh +75 -15
- package/assets/hooks/skill-loaded.sh +1 -0
- package/assets/hooks/sql-guard.sh +606 -56
- package/assets/hooks/task-context-load.sh +100 -0
- package/assets/hooks/task-flow-guard.sh +118 -0
- package/assets/laws/{access.md → application/access.md} +1 -4
- package/assets/laws/{locales.md → application/locales.md} +1 -3
- package/assets/laws/application/money.md +41 -0
- package/assets/laws/application/ownership.md +32 -0
- package/assets/laws/{search-visibility.md → application/search-visibility.md} +1 -1
- package/assets/laws/code-structure.md +7 -6
- package/assets/laws/delivery.md +53 -3
- package/assets/laws/entity-editing.md +49 -55
- package/assets/laws/entity-models.md +4 -14
- package/assets/laws/frontend-application.md +5 -5
- package/assets/laws/lib-imports.md +14 -1
- package/assets/laws/lists.md +33 -0
- package/assets/laws/navigation.md +40 -0
- package/assets/laws/project-documentation.md +27 -8
- package/assets/laws/reuse-first.md +26 -21
- package/assets/laws/shared-code.md +13 -1
- package/assets/laws/verifiability.md +30 -1
- package/assets/laws/work-conduct.md +59 -0
- package/assets/patterns/admin-lists-screen.md +131 -0
- package/assets/patterns/admin-nav-item.md +71 -0
- package/assets/patterns/angular-patterns-state.md +29 -22
- package/assets/patterns/api-layer-pair.md +40 -30
- package/assets/patterns/browser-verification-measure.md +41 -38
- package/assets/patterns/browser-verification-stand.md +106 -42
- package/assets/patterns/component-structure-new.md +33 -32
- package/assets/patterns/dependencies-upgrade.md +65 -0
- package/assets/patterns/doc-style-sweep.md +65 -28
- package/assets/patterns/doc-style-write.md +36 -33
- package/assets/patterns/entity-aside.md +136 -0
- package/assets/patterns/entity-models-new.md +124 -0
- package/assets/patterns/entity-store.md +91 -0
- package/assets/patterns/git-workflow-commit.azure.md +259 -0
- package/assets/patterns/git-workflow-commit.github.md +337 -0
- package/assets/patterns/git-workflow-commit.gitlab.md +283 -0
- package/assets/patterns/git-workflow-merge.md +42 -25
- package/assets/patterns/git-workflow-migration.md +61 -31
- package/assets/patterns/git-workflow-restart.md +20 -20
- package/assets/patterns/lib-layers-move.md +50 -32
- package/assets/patterns/lib-layers-new.md +41 -29
- package/assets/patterns/ownership-scope-resolve.md +69 -0
- package/assets/patterns/permissions-procedure.md +35 -33
- package/assets/patterns/platform-access-di.md +39 -25
- package/assets/patterns/pricing-quote.md +71 -0
- package/assets/patterns/reuse-first-extend.md +22 -22
- package/assets/patterns/seo-page.md +52 -40
- package/assets/patterns/seo-verify.md +48 -29
- package/assets/patterns/shared-code-new.md +37 -31
- package/assets/patterns/spec-driven-domain.md +60 -37
- package/assets/patterns/spec-driven-rule.md +55 -40
- package/assets/patterns/styling-bem-component.md +43 -32
- package/assets/patterns/styling-bem-layout.md +30 -24
- package/assets/patterns/task-flow-close.md +154 -0
- package/assets/patterns/task-flow-resume.md +94 -0
- package/assets/patterns/task-flow-start.md +129 -0
- package/assets/patterns/testing-e2e.md +53 -51
- package/assets/patterns/testing-unit.md +70 -46
- package/assets/patterns/translations-key.md +32 -19
- package/assets/patterns/ts-procedure.md +24 -25
- package/assets/rules/angular-patterns.md +50 -27
- package/assets/rules/api-layer.md +46 -28
- package/assets/rules/browser-verification.md +67 -48
- package/assets/rules/component-structure.md +43 -27
- package/assets/rules/dependencies.md +66 -0
- package/assets/rules/doc-style.md +95 -39
- package/assets/rules/entity-conventions.md +78 -0
- package/assets/rules/entity-models.md +70 -0
- package/assets/rules/git-workflow.azure.md +116 -0
- package/assets/rules/git-workflow.github.md +123 -0
- package/assets/rules/git-workflow.gitlab.md +113 -0
- package/assets/rules/lib-layers.md +56 -30
- package/assets/rules/lists.md +73 -0
- package/assets/rules/navigation.md +78 -0
- package/assets/rules/ownership-scope.md +63 -0
- package/assets/rules/permissions.md +43 -25
- package/assets/rules/platform-access.md +57 -29
- package/assets/rules/pricing.md +64 -0
- package/assets/rules/reuse-first.md +57 -43
- package/assets/rules/seo.md +51 -30
- package/assets/rules/shared-code.md +51 -26
- package/assets/rules/spec-driven.md +107 -51
- package/assets/rules/styling-bem.md +54 -39
- package/assets/rules/task-flow.md +150 -0
- package/assets/rules/testing.md +78 -47
- package/assets/rules/translations.md +48 -31
- package/assets/rules/typescript-conventions.md +57 -27
- package/assets/skills/agent-kit.md +85 -0
- package/assets/skills/write-a-skill.md +108 -0
- package/assets/templates/gate-map.sh +23 -15
- package/assets/templates/implementation.md +14 -8
- package/assets/templates/pattern.md +1 -1
- package/assets/templates/project.sh +32 -19
- package/assets/templates/rule.md +2 -2
- package/assets/variants.json +20 -0
- package/assets/workflows/feature.js +134 -0
- package/assets/workflows/plan.js +150 -0
- package/bin/agent-kit.d.ts.map +1 -1
- package/bin/agent-kit.js +78 -5
- package/bin/agent-kit.js.map +1 -1
- package/bin/prompt.d.ts +5 -0
- package/bin/prompt.d.ts.map +1 -1
- package/bin/prompt.js +19 -7
- package/bin/prompt.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/lib/assets.d.ts +8 -3
- package/lib/assets.d.ts.map +1 -1
- package/lib/assets.js +13 -3
- package/lib/assets.js.map +1 -1
- package/lib/catalog.d.ts +52 -5
- package/lib/catalog.d.ts.map +1 -1
- package/lib/catalog.js +104 -16
- package/lib/catalog.js.map +1 -1
- package/lib/commands.d.ts +22 -1
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +202 -14
- package/lib/commands.js.map +1 -1
- package/lib/companion.d.ts +5 -1
- package/lib/companion.d.ts.map +1 -1
- package/lib/companion.js +29 -2
- package/lib/companion.js.map +1 -1
- package/lib/config.d.ts +26 -9
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +41 -15
- package/lib/config.js.map +1 -1
- package/lib/freshness.d.ts +14 -0
- package/lib/freshness.d.ts.map +1 -0
- package/lib/freshness.js +116 -0
- package/lib/freshness.js.map +1 -0
- package/lib/hooks-map.d.ts +27 -0
- package/lib/hooks-map.d.ts.map +1 -0
- package/lib/hooks-map.js +77 -0
- package/lib/hooks-map.js.map +1 -0
- package/lib/integrity.d.ts +36 -0
- package/lib/integrity.d.ts.map +1 -0
- package/lib/integrity.js +44 -0
- package/lib/integrity.js.map +1 -0
- package/lib/picker.d.ts +11 -1
- package/lib/picker.d.ts.map +1 -1
- package/lib/picker.js +44 -6
- package/lib/picker.js.map +1 -1
- package/lib/sync.d.ts +26 -0
- package/lib/sync.d.ts.map +1 -1
- package/lib/sync.js +59 -4
- package/lib/sync.js.map +1 -1
- package/lib/variants.d.ts +44 -0
- package/lib/variants.d.ts.map +1 -0
- package/lib/variants.js +82 -0
- package/lib/variants.js.map +1 -0
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.5.0.tgz +0 -0
- package/assets/laws/admin-lists.md +0 -35
- package/assets/laws/admin-navigation.md +0 -38
- package/assets/patterns/git-workflow-commit.md +0 -175
- package/assets/rules/git-workflow.md +0 -106
- package/rt-tools-agent-kit-0.3.0.tgz +0 -0
package/assets/rules/testing.md
CHANGED
|
@@ -2,68 +2,99 @@
|
|
|
2
2
|
name: testing
|
|
3
3
|
kind: rule
|
|
4
4
|
law: verifiability
|
|
5
|
-
description: Правило под
|
|
5
|
+
description: Правило под «Закон о проверяемости». Брать при правке любого *.spec.ts и всего, что лежит в apps/site-e2e и apps/admin-e2e. Называет Vitest и Playwright, идентификатор сценария в заголовке, вынос решения в чистую функцию и то, что закрывается сквозной спекой. Готовый код — в паттернах testing-unit и testing-e2e.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# Проверяемость —
|
|
8
|
+
# Проверяемость — как это устроено здесь
|
|
9
9
|
|
|
10
|
-
Правило под закон `
|
|
11
|
-
здесь —
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
Правило под закон `docs/constitution/verifiability.md`. Закон говорит, что считается
|
|
11
|
+
подтверждением; здесь — чем это названо в этом дереве, где лежит и что из закона у нас не
|
|
12
|
+
применяется. Проверка работающего приложения глазами и замером — правило
|
|
13
|
+
`browser-verification` под тем же законом.
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Как это называется здесь
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
| В законе | Здесь |
|
|
18
|
+
| -------------------------- | -------------------------------------------------------------------------------- |
|
|
19
|
+
| сценарий | `SC-<ПРЕФИКС>-<НОМЕР>` в `docs/specs/<домен>/scenarios.md` |
|
|
20
|
+
| тест | `it(...)` в `*.spec.ts` рядом с исходником — Vitest; сквозная спека — Playwright |
|
|
21
|
+
| сводка покрытия | вывод `npm run check:specs`: покрыто, частично, без тестов |
|
|
22
|
+
| отметка непокрытого | строка `Не покрыто: <причина>` внутри блока сценария |
|
|
23
|
+
| отметка неполного покрытия | строка `Покрытие: частичное — <чего не хватает>` |
|
|
19
24
|
|
|
20
|
-
##
|
|
25
|
+
## Где это лежит
|
|
26
|
+
|
|
27
|
+
В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
|
|
28
|
+
переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
|
|
29
|
+
же дереве, которое держит код иначе.
|
|
30
|
+
|
|
31
|
+
## Как закон применяется здесь
|
|
21
32
|
|
|
22
33
|
- **Идентификатор сценария стоит в начале заголовка теста, через тире.** Один сценарий
|
|
23
|
-
проверяется несколькими тестами, один тест закрывает несколько
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
проверяется несколькими тестами, один тест закрывает несколько сценариев.
|
|
35
|
+
- **Сценарий без теста несёт отметку с причиной.** Пустая отметка не принимается, а отметка
|
|
36
|
+
при существующем тесте — отказ: она означает, что долг закрыли, а отметку не сняли.
|
|
37
|
+
- **Тест, идущий не тем путём, что пользователь, помечается частичным покрытием.** В сводку
|
|
38
|
+
он попадает долгом, а не покрытием.
|
|
39
|
+
- **Сценарий, чьё «Тогда» называет человека и то, что он видит, закрывается сквозным тестом.**
|
|
40
|
+
Юнит на тот же расчёт остаётся долгом: между верным решением и тем, что человек его видит,
|
|
41
|
+
лежит всё, чего юнит не касался.
|
|
42
|
+
- **Сквозной тест, погашенный переменной окружения, покрытием не считается.** Выключатель по
|
|
43
|
+
состоянию стенда — пропуск случая, выключатель по переменной — невыполненный тест.
|
|
29
44
|
- **Упоминание в тесте сценария, которого в спеках нет, роняет проверку.** Так ловится
|
|
30
45
|
переименованный или выкинутый сценарий: тесты при этом остаются зелёными.
|
|
31
46
|
- **Решение выносится в чистую функцию и проверяется вызовом.** Компонент и сервис остаются
|
|
32
47
|
тонкой обёрткой и отдельно не проверяются, пока своего ветвления у них нет.
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
- **Процедура Connect проверяется вызовом своего метода с рукописным двойником базы.**
|
|
49
|
+
Контейнер и роутер поднимать не надо: спека проверяет решение, а не раскладку полей.
|
|
50
|
+
- **Спека, необратимо меняющая данные стенда, выключена по умолчанию.** `BASE_URL` уводит
|
|
51
|
+
прогон одной переменной, и без выключателя такая спека правила бы данные чужого стенда.
|
|
52
|
+
- **Спеки, которым нужен nginx перед приложением, просыпаются вместе с `BASE_URL`.** Голый
|
|
53
|
+
сервер отдачи страниц их не проходит: перенаправления живут в конфиге прокси.
|
|
54
|
+
- **Правило линтера, которое запрещает принятый здесь приём, выключают в конфиге, а не
|
|
55
|
+
обходят в каждом тесте.** Выключатель теста — приём этого дерева, а
|
|
56
|
+
`playwright/no-skipped-test` запретил бы его сразу в шестидесяти пяти местах. Рядом со
|
|
57
|
+
строкой отключения пишут причину, а точечный `eslint-disable` остаётся для того, что
|
|
58
|
+
запрещено по делу.
|
|
59
|
+
|
|
60
|
+
## Чего из закона здесь нет
|
|
61
|
+
|
|
62
|
+
Полнота теста не проверяется: сверка судит путь — сквозной он или юнит, — но не то, сколько
|
|
63
|
+
из обещанного тест на этом пути закрыл. Признак пути читается из слов «Тогда» и нарочно
|
|
64
|
+
молчалив: сценарий, чьё обещание человека не называет, под него не подпадает вовсе, и
|
|
65
|
+
отметку неполноты там по-прежнему ставит рука. Договорённостей о подмене модулей тоже нет:
|
|
66
|
+
`vi.mock` в дереве не встречается ни разу, и двойник пишется руками.
|
|
44
67
|
|
|
45
68
|
## Паттерны
|
|
46
69
|
|
|
47
|
-
- `testing-unit` — тест на чистую функцию, на
|
|
48
|
-
|
|
49
|
-
- `testing-e2e` — прогон сквозных тестов, стенд под настоящим
|
|
70
|
+
- `testing-unit` — тест на чистую функцию, на процедуру Connect и разовый тест-доказательство,
|
|
71
|
+
который не коммитится.
|
|
72
|
+
- `testing-e2e` — прогон сквозных тестов, стенд под настоящим nginx, выключатели.
|
|
50
73
|
|
|
51
74
|
## Ловушки
|
|
52
75
|
|
|
53
|
-
- **Зелёный
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
|
|
76
|
+
- **Зелёный `nx test <проект>` не значит, что хоть один файл исполнялся.** Либа без своего
|
|
77
|
+
`vitest.config.mts` не запускает ничего — так тесты домена броней не запускались ни разу.
|
|
78
|
+
Либа с конфигом, но без единого `*.spec.ts`, проходит зелёной из-за `passWithNoTests: true`,
|
|
79
|
+
который стоит во всех 203 конфигах дерева, и на глаз эти два случая неотличимы: в обоих
|
|
80
|
+
прогон успешен. Без единого теста живут 131 либа из 203 — почти две трети. Перед правкой в
|
|
81
|
+
незнакомой либе проверяется, есть ли в ней хоть один `*.spec.ts`; если нет — первый
|
|
82
|
+
заводится этой же правкой, а не откладывается: откладывать здесь не с чего, долг уже
|
|
83
|
+
накоплен. Пересчёт: `for d in $(find libs -name vitest.config.mts -exec dirname {} \;); do
|
|
84
|
+
[ -z "$(find "$d" -name '*.spec.ts')" ] && echo "$d"; done | wc -l`.
|
|
85
|
+
- **«Executable doesn't exist» — состояние машины, а не дефект правки.** Установлен только
|
|
86
|
+
chromium, `firefox` и `webkit` падают всегда: гонять `--project=chromium`, узкий экран —
|
|
87
|
+
`--project=mobile-chrome`. Та же ошибка приходит после смены версии Playwright: браузер
|
|
88
|
+
ставится под конкретную версию, и после подъёма нужен повторный
|
|
89
|
+
`npx playwright install chromium`. Девять тестов так и упали, и это выглядело регрессией
|
|
90
|
+
обновления.
|
|
91
|
+
- **Первому прогону сразу после установки браузера верить нельзя.** Два падения `admin-e2e`
|
|
92
|
+
не повторились ни при отдельном прогоне тех же тестов, ни при втором полном. Такой прогон
|
|
93
|
+
повторяют, а выводы делают по второму.
|
|
94
|
+
- Сквозные тесты админки без `E2E_ADMIN_EMAIL` и `E2E_ADMIN_PASSWORD` пропускаются молча — в
|
|
95
|
+
отчёте они значатся `skipped`, и прогон выглядит успешным.
|
|
96
|
+
- **Справочник флоу вторых сценариев не заводит.** В `docs/E2E_<ДОМЕН>_FLOWS.md` кладут то,
|
|
97
|
+
чего в спеке домена нет и быть не должно: `qa-dataid` элементов, состояния разметки, ловушки
|
|
98
|
+
стенда. Обещанное поведение остаётся сценарием в `scenarios.md`: если списать его во второе
|
|
99
|
+
место, копии разойдутся молча — `npm run check:specs` этого не увидит.
|
|
100
|
+
- `npx nx serve` проверкой не является: это шаг из правила `browser-verification`, а не тест.
|
|
@@ -2,51 +2,68 @@
|
|
|
2
2
|
name: translations
|
|
3
3
|
kind: rule
|
|
4
4
|
law: locales
|
|
5
|
-
description: Правило под
|
|
5
|
+
description: Правило под «Закон о локалях и переводах». Брать при заведении любого видимого текста, правке словарей libs/common/i18n, префиксов локалей сайта и перевода контента объекта. Называет восемь локалей, Transloco, производные переводы и начальную валюту локали. Готовый код — в паттерне translations-key.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# Локали и переводы —
|
|
8
|
+
# Локали и переводы — как это устроено здесь
|
|
9
9
|
|
|
10
|
-
Правило под закон `
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
Правило под закон `docs/constitution/application/locales.md`. Закон говорит, что должно быть верно;
|
|
11
|
+
здесь — чем это названо в этом дереве и где лежит. Разметка сайта для поиска — `seo` под
|
|
12
|
+
своим законом.
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Как это называется здесь
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
| В законе | Здесь |
|
|
17
|
+
| -------------------- | --------------------------------------------------------------------------------------------------- |
|
|
18
|
+
| локаль сайта | одна из восьми: `en`, `ru`, `de`, `zh-Hans`, `zh-Hant`, `ko`, `th`, `hi` |
|
|
19
|
+
| локаль по умолчанию | `en` — отдаётся из корня, без префикса в адресе |
|
|
20
|
+
| локаль ввода | `source_locale` в запросе сохранения объекта; берётся из языка админки |
|
|
21
|
+
| словарь | JSON-словари Transloco в `libs/common/i18n/src/lib/dictionaries/<локаль>/`, разложенные по разделам |
|
|
22
|
+
| перевод контента | jsonb по локалям в базе, заполняется бэкендом |
|
|
23
|
+
| подстановка в шаблон | `\| transloco` в разметке, `translateSignal` в классе |
|
|
18
24
|
|
|
19
|
-
##
|
|
25
|
+
## Где это лежит
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
|
|
28
|
+
переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
|
|
29
|
+
же дереве, которое держит код иначе.
|
|
30
|
+
|
|
31
|
+
## Как закон применяется здесь
|
|
32
|
+
|
|
33
|
+
- **Локаль по умолчанию отдаётся из корня, остальные семь — из-под префикса пути.**
|
|
34
|
+
- **Видимый человеку текст берётся из словаря и заводится во всех локалях перевода.** Ключ,
|
|
35
|
+
потерянный в одной локали, гость видит на кнопке как есть.
|
|
36
|
+
- **Пустой перевод считается пропуском, а не переводом.** Запасной словарь подставляется
|
|
37
|
+
только на отсутствующий ключ, а пустую строку отдаёт как есть.
|
|
38
|
+
- **Переводы контента производные: владелец пишет на своём языке, остальные семь локалей
|
|
39
|
+
заполняет бэкенд при сохранении.** Табов локалей в формах админки нет — правка вручную всё
|
|
40
|
+
равно была бы перезаписана.
|
|
29
41
|
- **Отказ перевода сохранение не срывает.** Текст на локали ввода записывается, прежние
|
|
30
42
|
переводы остаются, и владелец видит предупреждение, а не сообщение об успехе.
|
|
31
43
|
- **Локаль, для которой перевода не пришло, сохраняет прежнее значение.**
|
|
32
|
-
- **Язык
|
|
33
|
-
ввода при сохранении
|
|
34
|
-
- **У локали есть начальная валюта показа.** Дальше валюту выбирает
|
|
44
|
+
- **Язык админки выбирает владелец, и выбор живёт в его профиле.** Он же уходит в локаль
|
|
45
|
+
ввода при сохранении объекта.
|
|
46
|
+
- **У локали есть начальная валюта показа.** Дальше валюту выбирает гость, и его выбор
|
|
35
47
|
сильнее умолчания локали.
|
|
36
48
|
|
|
49
|
+
## Чего из закона здесь нет
|
|
50
|
+
|
|
51
|
+
Страница не показывает, что перевод устарел: владелец правку сохранил, провайдер отказал —
|
|
52
|
+
и гость по-прежнему видит старый текст. Это `Q-L-1` в законе.
|
|
53
|
+
|
|
37
54
|
## Паттерны
|
|
38
55
|
|
|
39
|
-
- `translations-key` — заведение ключа во всех локалях и подстановка в разметку.
|
|
56
|
+
- `translations-key` — заведение ключа во всех локалях перевода и подстановка в разметку.
|
|
40
57
|
|
|
41
58
|
## Ловушки
|
|
42
59
|
|
|
43
|
-
- **Полноту словарей держит не
|
|
44
|
-
недостающем или пустом ключе;
|
|
45
|
-
- **Наборы ключей сверяются внутри раздела**, а не по всему словарю сразу: словари разложены
|
|
46
|
-
общий,
|
|
47
|
-
- **Перевод
|
|
48
|
-
|
|
49
|
-
- **Без ключа
|
|
50
|
-
|
|
51
|
-
-
|
|
52
|
-
|
|
60
|
+
- **Полноту словарей держит не рантайм, а тест.** `nx test common-i18n` роняет сборку на
|
|
61
|
+
недостающем или пустом ключе; дозаполнить недостающее — `npm run i18n:fill`.
|
|
62
|
+
- **Наборы ключей сверяются внутри раздела**, а не по всему словарю сразу: словари разложены
|
|
63
|
+
на общий, сайт, письма и админку.
|
|
64
|
+
- **Перевод контента идёт до транзакции сохранения:** страница объекта не должна оказаться
|
|
65
|
+
наполовину переведённой. Кэш сбрасывается после записи и один раз.
|
|
66
|
+
- **Без ключа `ANTHROPIC_API_KEY` сохранение проходит,** но переводы остаются прежними, и
|
|
67
|
+
владелец видит предупреждение `propertySaveTranslationFailed`.
|
|
68
|
+
- Новый маршрут сайта без ветки под каждую локаль существует только в локали по умолчанию:
|
|
69
|
+
`/de/<путь>` отдаст 404 и поисковику, и гостю.
|
|
@@ -2,45 +2,75 @@
|
|
|
2
2
|
name: typescript-conventions
|
|
3
3
|
kind: rule
|
|
4
4
|
law: code-structure
|
|
5
|
-
description: Правило под
|
|
5
|
+
description: Правило под «Закон об устройстве кода». Брать при правке любого .ts, кроме компонента, сервиса, директивы, пайпа, гарда и интерцептора — строгая типизация, модификаторы доступа, приватные поля, имена файлов, префиксы, перечисления, запрет приведения в маппере, процедуры Connect на бэкенде. Готовый код процедуры — в паттерне ts-procedure.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# Устройство кода —
|
|
8
|
+
# Устройство кода — как это устроено здесь
|
|
9
9
|
|
|
10
|
-
Правило под закон `
|
|
11
|
-
объявления; здесь — как это
|
|
12
|
-
лежат — `implementation.md` рядом.
|
|
10
|
+
Правило под закон `docs/constitution/code-structure.md`. Закон говорит, что должно быть верно
|
|
11
|
+
про объявления; здесь — как это записано в этом дереве.
|
|
13
12
|
|
|
14
|
-
##
|
|
13
|
+
## Как это называется здесь
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
| В законе | Здесь |
|
|
16
|
+
| --------------------------- | --------------------------------------------------------- |
|
|
17
|
+
| признак рода в имени | префикс: `I` у интерфейса, `E` у перечисления, `T` у типа |
|
|
18
|
+
| источник, за которым следят | суффикс `$` у наблюдаемого и `Source` у субъекта |
|
|
19
|
+
| приватное поле | `#field`, а не `private field` |
|
|
20
|
+
| процедура бэкенда | класс с меткой `@ConnectProcedure()`, один на процедуру |
|
|
18
21
|
|
|
19
|
-
##
|
|
22
|
+
## Где это лежит
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
|
|
25
|
+
переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
|
|
26
|
+
же дереве, которое держит код иначе.
|
|
27
|
+
|
|
28
|
+
## Как закон применяется здесь
|
|
29
|
+
|
|
30
|
+
- **Род объявления виден по префиксу имени, и это держат три правила линтера.** У интерфейса,
|
|
31
|
+
у типа и у перечисления свои; все три подняты для всех `.ts`.
|
|
32
|
+
- **Источник, за которым следят, назван суффиксом.** Субъект и наблюдаемое, поднятое из него,
|
|
33
|
+
различаются в месте использования, а не переходом к объявлению.
|
|
34
|
+
- **Суффикс имени файла находит в нём обещанное объявление.** Список суффиксов закрыт: слово,
|
|
35
|
+
которого в нём нет, суффиксом не считается, и такой файл правило не судит.
|
|
25
36
|
- **Тип берётся из того пакета, где объявлен.** Своя копия чужого типа расходится с оригиналом
|
|
26
37
|
молча, а компилируется из них только одна.
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
|
|
38
|
+
- **Двухступенчатое приведение `as unknown as` запрещено правилом линтера.** Вместо него —
|
|
39
|
+
честный тип, сужение проверкой или чтение поля формой (`Reflect.get`); место, где иначе
|
|
40
|
+
нельзя, помечается точечным отключением с причиной в той же строке.
|
|
41
|
+
- **Отметка об устаревании ставится вместе с обходом потребителей.** Все правила
|
|
42
|
+
`eslint-plugin-sonarjs` подняты до отказа разом, и пометка на типе красит каждое место, где
|
|
43
|
+
его ещё зовут: один `@deprecated` на файл дал семнадцать замечаний в чужих доменах.
|
|
44
|
+
|
|
45
|
+
## Чего из закона здесь нет
|
|
46
|
+
|
|
47
|
+
Одноступенчатое приведение остаётся непроверенным — это `Q-CS-4` в законе: из семидесяти
|
|
48
|
+
шести приведений восемь обязательны, и общий запрет отбивал бы их.
|
|
49
|
+
|
|
50
|
+
Тесты из-под запрета выведены целиком: рукописный двойник базы — принятый здесь приём, и
|
|
51
|
+
запрет пришлось бы обходить в каждом из тридцати пяти.
|
|
52
|
+
|
|
53
|
+
Правило имён файлов судит обещание, а не его отсутствие: `menu.items.ts` и `sign-in.ts` под
|
|
54
|
+
него не подпадают вовсе — это `Q-CS-3` в законе. Из двух принятых здесь форм перевода
|
|
55
|
+
сущности — класс на фронте и чистые функции на бэкенде — правило принимает обе: оно судит
|
|
56
|
+
имя, а не устройство, и что форм две, остаётся вопросом `Q-S-1` в законе об общем коде.
|
|
57
|
+
|
|
58
|
+
На `libs/api/**` и `apps/api/**` правило действует целиком, а вот сигнальный API и `inject`
|
|
59
|
+
туда не относятся: там NestJS с конструкторным DI.
|
|
32
60
|
|
|
33
61
|
## Паттерны
|
|
34
62
|
|
|
35
|
-
- `ts-procedure` — завести
|
|
63
|
+
- `ts-procedure` — завести процедуру Connect на бэкенде: класс, метка, право, регистрация.
|
|
36
64
|
|
|
37
65
|
## Ловушки
|
|
38
66
|
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
|
|
67
|
+
- Приведение через `as Type` в маппере запрещено, но не стережётся ничем: оно принимает любое
|
|
68
|
+
значение и компилируется. Вместо него — `this.typeCast`.
|
|
69
|
+
- Неиспользуемый параметр убирается, а не переименовывается: подчёркивание перед именем прячет
|
|
70
|
+
замечание, но параметр остаётся в сигнатуре.
|
|
71
|
+
- Агрегат Prisma своим типом не аннотируется: сгенерированный тип у него шире, чем результат
|
|
72
|
+
выборки, и аннотация врёт.
|
|
73
|
+
- Своё правило линтера включается вместе с переводом всех, кого оно ловит: включённое поверх
|
|
74
|
+
накопленного даёт красный прогон на файлах, которых правка не касалась.
|
|
75
|
+
- `#field` виден только внутри класса и не принимается `viewChild` — там поле объявляется
|
|
76
|
+
`protected`.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent-kit
|
|
3
|
+
description: Переносимый слой правил агента — законы, правила, хуки и проверки, которые везёт пакет, а дерево настраивает надстройками. Брать, когда правится файл с шапкой rt-kit, обновляется пакет, отказывает `sync --check`, или своё поведение надо дописать поверх пакетного. Как заводится сам скил — write-a-skill; устройство слоёв текста — spec-driven.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Переносимый слой правил
|
|
7
|
+
|
|
8
|
+
Законы, правила, паттерны, хуки, проверки и роли везёт пакет; дерево берёт их раскладкой и
|
|
9
|
+
настраивает надстройками. Разложенный файл несёт шапку и правке не подлежит — правится либо
|
|
10
|
+
пакет, либо надстройка.
|
|
11
|
+
|
|
12
|
+
## Когда брать
|
|
13
|
+
|
|
14
|
+
- В файле, который собираешься править, стоит шапка
|
|
15
|
+
`rt-kit v<версия> · <ресурс> · <сумма> · правится надстройкой, не здесь`.
|
|
16
|
+
- Обновился пакет, или `sync --check` отказал в гейте пуша.
|
|
17
|
+
- Своё поведение надо дописать поверх пакетного: гард, карта гейта, набор проверок.
|
|
18
|
+
- Пакет ставится в дерево, где хуки и проверки уже свои.
|
|
19
|
+
|
|
20
|
+
## Где что настраивается
|
|
21
|
+
|
|
22
|
+
| Что меняешь | Куда правка |
|
|
23
|
+
| ---------------------------------------------- | -------------------------------------------------- |
|
|
24
|
+
| какое правило гейт требует под какой файл | `.claude/rt-kit/gate-map.sh` — своя `skill_for` |
|
|
25
|
+
| порты, адреса, линтеры, форма ветки, инвентарь | `.claude/rt-kit/project.sh` — свои `rt_*` |
|
|
26
|
+
| пути и идентификаторы, которыми живут проверки | `.claude/rt-kit/checks.json` |
|
|
27
|
+
| раздел разложенного текста | `.claude/rt-kit/overrides/<идентификатор ресурса>` |
|
|
28
|
+
| что брать, а от чего отказаться | `.claude/rt-kit.json`, ключи `only` и `skip` |
|
|
29
|
+
| сам механизм — гард, проверка, текст правила | ресурс в пакете |
|
|
30
|
+
|
|
31
|
+
Надстройка объявляет функцию заново и вправе позвать умолчание тем же именем с суффиксом
|
|
32
|
+
`_default`. Слияние текста идёт по разделам `## `: совпавший заголовок замещает, новый
|
|
33
|
+
дописывается, пустой снимает раздел пакета.
|
|
34
|
+
|
|
35
|
+
## Порядок
|
|
36
|
+
|
|
37
|
+
Правка ресурса доезжает до дерева только через сборку пакета: строка запуска читает собранное,
|
|
38
|
+
а не исходники ресурсов. Порядок один и тот же всегда — правка, сборка, `sync`. Отказ хотя бы
|
|
39
|
+
по одному файлу не пишет ничего: половина разложенного хуже целого.
|
|
40
|
+
|
|
41
|
+
## Команды
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx agent-kit doctor # что разложено, что отстало, что лежит от отказанного
|
|
45
|
+
npx agent-kit sync # разложить
|
|
46
|
+
npx agent-kit sync --check # ничего не писать, отказать при расхождении
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Установка туда, где уже всё своё
|
|
50
|
+
|
|
51
|
+
1. `init`, затем `skip` на всё, что дерево держит само. Пустая раскладка — законное начало.
|
|
52
|
+
2. Снимок того, что говорят проверки дерева, до единой правки. Он и есть мерило.
|
|
53
|
+
3. Ресурс за ресурсом: пакетную редакцию довести до здешней, предметность вынести в
|
|
54
|
+
надстройку, снять отказ, разложить, прогнать сценарии и сверить снимок.
|
|
55
|
+
4. Разложенное поверх своего пакет не пишет: файл без шапки для него чужой. Снять его —
|
|
56
|
+
решение владельца, и до этого раскладка отказывает поимённо.
|
|
57
|
+
|
|
58
|
+
## Ловушки
|
|
59
|
+
|
|
60
|
+
- **Линтер по следам правки судит файл целиком, а не внесённую правку.** Импорт, добавленный
|
|
61
|
+
отдельным шагом, отбивается как неиспользуемый ещё до того, как появится строка, которая его
|
|
62
|
+
зовёт, и работа встаёт на половине. Правка делается одним вызовом либо в порядке «сначала
|
|
63
|
+
использование, потом импорт».
|
|
64
|
+
- **Прогонять сценарии гардов можно, ничего не раскладывая.** Обвязка набора принимает каталог
|
|
65
|
+
гардов переменной, и пакетную редакцию гоняют по сценариям дерева до установки. Заход,
|
|
66
|
+
потраченный на диагноз по последствиям, стоил ровно этой строки.
|
|
67
|
+
- **Отбитая правка не всегда про текст гарда.** Гард зовут по пути, и файл без права на
|
|
68
|
+
запуск отвечает отказом доступа — ненулевым кодом, который читается как «правка отбита».
|
|
69
|
+
Набор при этом отбивает подряд всё, включая сборку и тесты, и причины не называет.
|
|
70
|
+
- **Правка shell-скрипта заменой по шаблону сверяется `bash -n` сразу.** Замена границ
|
|
71
|
+
конструкции не видит: `case` теряет свою `esac`, файл остаётся синтаксически неверным, а
|
|
72
|
+
гард с ошибкой синтаксиса отвечает ненулевым кодом — то есть «правка отбита». Два раза за
|
|
73
|
+
заход, и оба раза это выглядело дефектом самого гарда.
|
|
74
|
+
- **Разложенный файл узнаётся по шапке, а не по каталогу.** Раскладка ложится в те же
|
|
75
|
+
`tools/`, `.claude/hooks/` и `.claude/skills/`, где лежит своё, поэтому карта гейта,
|
|
76
|
+
написанная по путям, требует под него доменное правило — а оно уводит править файл на месте.
|
|
77
|
+
Правка на месте теряется на следующей раскладке, и до тех пор выглядит применённой. Ветка по
|
|
78
|
+
шапке ставится в карте первой и решает раньше путей.
|
|
79
|
+
- **Настройки проверок сливаются на один уровень.** Верхние ключи `checks.json` ложатся поверх
|
|
80
|
+
умолчаний по одному, а вложенный объект замещается целиком: назвав один ключ борды, дерево
|
|
81
|
+
теряет остальные — и увидит это отказом «нет токена бота», то есть как неполадку машины.
|
|
82
|
+
Вложенный раздел заполняется целиком либо не заводится вовсе.
|
|
83
|
+
- **Утверждение правила переезжает вместе с кодом.** Вынесенное в надстройку перестаёт
|
|
84
|
+
находиться по прежнему символу, и привязка в спутнике правила врёт молча — сверка спеков
|
|
85
|
+
ловит это, но только если её позвать.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: write-a-skill
|
|
3
|
+
description: Заведение нового скила — того, у которого нет закона над собой: витрина, генератор, чужой сервис, приём работы этого дерева. Брать, когда просят завести, написать или переписать скил. Форма правила и паттерна сюда не входит — это паттерн spec-driven-rule.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Как заводится скил
|
|
7
|
+
|
|
8
|
+
Скил — это то, что агент загружает перед работой и читает целиком. Отсюда всё остальное: он
|
|
9
|
+
короткий, он объявляет, когда его брать, и он не пересказывает то, что уже написано в соседнем.
|
|
10
|
+
|
|
11
|
+
## Когда брать
|
|
12
|
+
|
|
13
|
+
- Заводится скил, у которого нет закона над собой: витрина, генератор, работа с чужим сервисом,
|
|
14
|
+
приём, принятый в этом дереве.
|
|
15
|
+
- Скил разросся, и его пора делить.
|
|
16
|
+
- Скил есть, но его никто не загружает — надо чинить объявление.
|
|
17
|
+
|
|
18
|
+
**Правило и паттерн сюда не идут.** Правило стоит под законом, паттерн — при правиле, и форму
|
|
19
|
+
обоих держит паттерн `spec-driven-rule`. Скил без закона — третий случай, и только он здесь.
|
|
20
|
+
|
|
21
|
+
## Порядок
|
|
22
|
+
|
|
23
|
+
1. **Спроси, чего не хватает.** Какую работу скил закрывает, на чём спотыкались без него, нужны
|
|
24
|
+
ли готовые команды или хватает порядка действий. Скил, написанный без этого, пересказывает
|
|
25
|
+
документацию инструмента — а её агент и так знает.
|
|
26
|
+
2. **Напиши черновик.** Один файл. Дополнительные — только когда первый перестаёт читаться
|
|
27
|
+
целиком.
|
|
28
|
+
3. **Покажи владельцу.** Скил действует на все будущие сессии, и заводить его молча нельзя.
|
|
29
|
+
|
|
30
|
+
## Что в файле
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
<имя-скила>/
|
|
34
|
+
├── SKILL.md # обязателен, и обычно единственный
|
|
35
|
+
├── <ЧТО-ТО>.md # отдельный файл — когда SKILL.md перестал читаться целиком
|
|
36
|
+
└── scripts/ # готовые скрипты, если операция детерминированная
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Вступление между `---` — `name` и `description`, дальше заголовок и разделы. Первым разделом —
|
|
40
|
+
«Когда брать»: агент решает по нему, а не по названию.
|
|
41
|
+
|
|
42
|
+
## Объявление решает всё
|
|
43
|
+
|
|
44
|
+
`description` — **единственное, что агент видит**, когда решает, грузить скил или нет. Он стоит
|
|
45
|
+
в системном приглашении рядом с описаниями всех остальных, и выбор идёт по ним.
|
|
46
|
+
|
|
47
|
+
Оно отвечает на два вопроса: что скил даёт и когда его брать. Пиши третьим лицом, до 1024
|
|
48
|
+
знаков: первая фраза — что делает, вторая — «Брать, когда…», третья — чего в нём нет и где это
|
|
49
|
+
искать.
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
✓ Заведение нового скила — того, у которого нет закона над собой. Брать, когда просят
|
|
53
|
+
завести, написать или переписать скил. Форма правила и паттерна сюда не входит — это
|
|
54
|
+
паттерн spec-driven-rule.
|
|
55
|
+
|
|
56
|
+
✗ Помогает со скилами.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Второе объявление не даёт агенту ни одного способа отличить этот скил от соседнего — и он не
|
|
60
|
+
загрузит ни один.
|
|
61
|
+
|
|
62
|
+
Строка «чего здесь нет» не украшение: скилов в дереве десятки, и половина ошибок выбора — это
|
|
63
|
+
взятый соседний. Назови его по имени.
|
|
64
|
+
|
|
65
|
+
## Готовые команды вместо описания
|
|
66
|
+
|
|
67
|
+
Скрипт кладётся рядом со скилом, когда операция детерминированная — проверка, приведение к
|
|
68
|
+
форме, разбор вывода. Такое дешевле позвать, чем породить заново, и ошибку в нём видно один раз,
|
|
69
|
+
а не в каждом порождении.
|
|
70
|
+
|
|
71
|
+
Порядок действий, зависящий от того, что нашлось, скриптом не становится: он остаётся текстом.
|
|
72
|
+
|
|
73
|
+
## Когда делить
|
|
74
|
+
|
|
75
|
+
Делить, когда:
|
|
76
|
+
|
|
77
|
+
- файл перестал читаться целиком — по опыту это около сотни строк;
|
|
78
|
+
- в нём два разных предмета, и второй нужен раз в месяц;
|
|
79
|
+
- готовый код занял больше места, чем объяснение, зачем он.
|
|
80
|
+
|
|
81
|
+
Не делить ради красоты: два файла вместо одного стоят одного лишнего перехода при каждом
|
|
82
|
+
чтении, а ссылка глубже одного уровня не читается вовсе.
|
|
83
|
+
|
|
84
|
+
## Чем скил вредит
|
|
85
|
+
|
|
86
|
+
- **Пересказ документации инструмента.** Агент читает скил при каждой работе; строка, которую он
|
|
87
|
+
знает и без того, вытесняет ту, которой не знает.
|
|
88
|
+
- **Дублирование соседа.** Два скила об одном начинают противоречить друг другу, и первым это
|
|
89
|
+
замечает тот, кто выполнил оба.
|
|
90
|
+
- **То, что устареет вместе с кодом.** Скил, называющий версию, число или имя файла, которое
|
|
91
|
+
правится чаще раза в квартал, врёт молча.
|
|
92
|
+
- **Длина.** Скил в три экрана перестают читать целиком, а читают до первого знакомого раздела.
|
|
93
|
+
|
|
94
|
+
## Перед тем как отдать
|
|
95
|
+
|
|
96
|
+
- [ ] в объявлении есть «Брать, когда…» и назван сосед, которого брать не надо;
|
|
97
|
+
- [ ] первый раздел — «Когда брать»;
|
|
98
|
+
- [ ] файл читается целиком за один заход;
|
|
99
|
+
- [ ] ни одной строки, которая повторяет соседний скил;
|
|
100
|
+
- [ ] ничего, что устареет само собой: версий, чисел, сроков;
|
|
101
|
+
- [ ] есть хотя бы один готовый пример, а не только объяснение;
|
|
102
|
+
- [ ] ссылки уходят на один уровень, не глубже.
|
|
103
|
+
|
|
104
|
+
## Чтобы скил загружали
|
|
105
|
+
|
|
106
|
+
Написанного мало: скил, который никто не открывает, не действует. Новый скил либо называется в
|
|
107
|
+
карте гейта этого дерева, либо его зовёт по имени другой скил или роль. Скил, не названный
|
|
108
|
+
нигде, останется декорацией.
|