@ragnoryok1/dsh-client-locale-ru 0.2.12 → 0.2.14

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/CHANGELOG.md CHANGED
@@ -1,1341 +1,1534 @@
1
- # Changelog
2
-
3
- Все значимые изменения пакета. Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/),
4
- версии — по [SemVer](https://semver.org/lang/ru/).
5
-
6
- Пакет следует за харнессом: целевая версия — `dsh 0.2.1-alpha.1` и новее
7
- (`>=0.1.0-rc.2 <0.3.0` для `@deepseek-ai/dsh-client-locale`).
8
-
9
- ## [0.2.12] — 2026-10-20
10
-
11
- ### Добавлено
12
-
13
- - **Названия и описания сторонних плагинов — на русском.** Проход, который подставляет
14
- русское название плагина в отрисованный интерфейс, знал только 32 названия плагинов
15
- самого харнесса. Теперь в `src/client/plugin-names-ru.json` лежат названия и описания
16
- ещё 32 сторонних плагинов — тех, чьи словари пакет переводит: английский текст взят
17
- из их собственной публикации, русский — из того же словаря. Файл собирается
18
- (`npm run build:plugin-names`), а гейт `npm run check:plugin-names:map` падает, когда
19
- карта разошлась со словарями. Одно и то же английское название не может быть
20
- переведено дважды: по-разному у харнесса и у плагина — сборка падает.
21
-
22
- ### Исправлено
23
-
24
- - **Проверка собранного бандла перестала врать о больших файлах.** `git show` бандла
25
- больше мегабайта упирался в мегабайтный буфер `spawnSync` и завершался ошибкой, а
26
- проверка читала это как «файла нет в коммите»: бандл любого размера выше лимита нельзя
27
- было сверить с коммитом.
28
-
29
- ## [0.2.11] — 2026-10-20
30
-
31
- ### Добавлено
32
-
33
- - **Словари сторонних плагинов: 59.** За этот релиз их стало на 27 больше:
34
- `@noob-stupid/dsh-plugin-console` (627 ключей), `@goodandready/dsh-subscriptions` (293 ключей), `@goodandready/dsh-key-rotation` (251 ключей), `@mars-sea/dsh-commandcode-provider` (224 ключей), `@goodandready/dsh-clinebot` (206 ключей), `@goodandready/dsh-remote-workspace` (201 ключей),
35
- `@goodandready/dsh-server-monitor` (168 ключей), `@goodandready/dsh-model-sync` (167 ключей), `@goodandready/dsh-live-canvas` (161 ключей), `@goodandready/dsh-messenger-gateway` (138 ключей), `@goodandready/dsh-gitea` (132 ключей), `@goodandready/dsh-vision-bridge` (116 ключей),
36
- `@goodandready/dsh-session-control` (113 ключей), `@goodandready/dsh-github-ops` (109 ключей), `@opencode2dsh/dsh-plugin` (102 ключей), `@goodandready/dsh-issue-reporter` (99 ключей), `@goodandready/dsh-goal` (97 ключей), `@goodandready/dsh-web-gateway` (95 ключей),
37
- `@goodandready/dsh-shadow-auditor` (74 ключей), `@goodandready/dsh-grok-xsearch` (55 ключей), `@goodandready/dsh-time-machine` (41 ключей), `@goodandready/dsh-approval-gate` (30 ключей), `@goodandready/dsh-smart-restart` (29 ключей), `@wenaixi/dsh-superpower` (19 ключей),
38
- `@goodandready/dsh-lanmode` (18 ключей), `@goodandready/dsh-context-lens` (11 ключей), `@vlln/dsh-navbar` (2 ключей).
39
- Всего 123 namespace и 12 590 строк, из них 9 557 — в словарях плагинов,
40
- 3 033 — в namespace самого харнесса.
41
-
42
- ### Исправлено
43
-
44
- - **Аудит больше не теряет ключи словарей плагинов.** Слова `name`, `key`, `type` и
45
- `interface` отфильтровывались как конструкции TypeScript, но в словаре плагина это
46
- обычные ключи локализации: четыре значения в `dsh-server-monitor` и `dsh-vision-bridge`
47
- не проверялись ни одним правилом аудита и считались отсутствующими в своём namespace.
48
- Теперь фильтр применяется только к namespace самого харнесса.
49
-
50
- ## [0.2.10] — 2026-10-19
51
-
52
- ### Добавлено
53
-
54
- - **Словари сторонних плагинов: 32.** За этот релиз их стало на семь больше:
55
- `dsh-better-sidebar` (627 ключей), `billion-context` (481), `@goodandready/dsh-kanban` (420),
56
- `@goodandready/dsh-cron` (396), `@goodandready/dsh-image-gen` (330), `@goodandready/dsh-voice` (321)
57
- и `dsh-univer-office` — четыре новых ключа. Всего 96 namespace и 9012 строки, из них 5979 —
58
- в словарях плагинов.
59
-
60
- ### Исправлено
61
-
62
- - **Проверка лишних пар форм больше не путает обычный ключ с формой.** Ключ `card.other`
63
- («Other clients») у плагина `billion-context` оканчивается на `.other` — так же, как форма
64
- множественного числа, — но основания `card` рядом нет, и проверка называла это дрейфом.
65
- Теперь вопрос задаётся шаблону: форма, у которой нет основы в самом плагине, — обычный
66
- ключ этого плагина.
67
- - **Устаревшее разрешение латинского слова убрано:** «install» разрешён теперь везде, и
68
- прежнее точечное разрешение больше ничего не разрешало.
69
-
70
- ## [0.2.9] — 2026-10-19
71
-
72
- ### Добавлено
73
-
74
- - **Словари сторонних плагинов: 25.** Пакет переводит не только namespace харнесса, но и словари
75
- сторонних плагинов: их английский текст выгружается из публикации плагина вместе с версией и
76
- лицензией (`npm run fetch:plugins`), а перевод сверяется именно с этим снимком, а не с чужим
77
- русским. В этом релизе добавлены: `dshmarket` (733 ключа), `@goodandready/dsh-tts` (226),
78
- `@goodandready/dsh-moa` (159), `dsh-better-input` (123), `@goodandready/dsh-cost-meter` (85),
79
- `dsh-ego-browser` (75), `@goodandready/dsh-usage-guard` (42), `dsh-config-manager` (36),
80
- `dsh-personal-directive` (5).
81
- - **Инструмент выгрузки словарей читает чужие форматы.** Ключ, записанный как идентификатор
82
- (`cancel: "Cancel"`), не попадал в снимок — часть словарей читалась пустыми; регистрация языка
83
- по одному (`locale.register(NS, "en", en)`) и константа namespace из другого файла пакета тоже
84
- не распознавались; клиентский бандл не обязательно лежит в `lib/` — `dshmarket` держит его в
85
- `client/`, и скан только `lib/` находил там словарь хостового модуля. Теперь снимок совпадает с
86
- тем, что плагин показывает.
87
-
88
- ### Исправлено
89
-
90
- - **Отчёт о названиях плагинов больше не считает переведённые непереведёнными.**
91
- `npm run report:descriptors` перечислял все читаемые названия как непереведённые, даже те, у
92
- которых в дереве харнесса уже лежал `locale/ru.json`. Теперь отчёт делит их на переведённые и
93
- оставшиеся и говорит число каждых.
94
- - **`dsh-plugins-store` убран из покрытия:** то, что выгрузка приняла за словарь, оказалось
95
- фрагментом JSON Schema — переводить схему значит сломать её.
96
-
97
- ## [0.2.8] — 2026-10-19
98
-
99
- ### Добавлено
100
-
101
- - **Названия плагинов переводятся без правки харнесса.** Название плагина приходит из его
102
- собственного файла `locale/<язык>.json`, а служба локализации разрешает эту карту, не
103
- обращаясь к зарегистрированным словарям, — поэтому словарь мог перевести строки плагина,
104
- но не его имя. Пакет несёт 32 названия сам: английский текст, из которого сделан перевод,
105
- и русский. Отрисованный интерфейс проходит через проход, который подставляет русский на
106
- место английского: карточки плагинов, строки состава bundle, страница плагина, реестр в
107
- настройках, имя для скринридера. Проход берёт только точное совпадение, поэтому фраза, в
108
- которой название лишь упомянуто, остаётся как была; код, поля ввода и переписка не
109
- трогаются. Как и типографский, проход работает только пока активен русский. Проверено на
110
- настоящем документе: `npm run check:plugin-names` (jsdom, 11 случаев).
111
- - **Патч для харнесса собирается, а не пишется руками.** `docs/harness-descriptors-ru.patch` —
112
- 32 файла `locale/ru.json` для переведённых плагинов — теперь строится из переводов и дерева
113
- харнесса (`npm run build:descriptor-patch`), а гейт `npm run check:descriptor-patch` падает,
114
- когда собранный патч и лежащий в репозитории разошлись. Гейт доказан в обе стороны: с
115
- изменённым переводом и с вырезанной строкой в патче.
116
-
117
- ### Исправлено
118
-
119
- - **Отчёт о названиях плагинов больше не считает переведённые.** `npm run report:descriptors`
120
- перечислял все читаемые названия как непереведённые, даже те, у которых в дереве харнесса
121
- уже лежал `locale/ru.json`. Теперь отчёт делит их на переведённые и оставшиеся и говорит
122
- число каждых.
123
-
124
- ## [0.2.7] — 2026-10-19
125
-
126
- ### Исправлено
127
-
128
- - **Проход типографики больше не правит переписку.** Он переписывал текстовые узлы во всём
129
- документе, кроме кода, полей ввода и редактируемого содержимого, — а переписка в это
130
- исключение не попадала: кавычки и тире превращались в «ёлочки» и em dash внутри сообщений,
131
- и в словах пользователя, и в ответе модели. Пакет правит интерфейс, а чужой текст
132
- принадлежит автору. Харнесс сам помечает область переписки (`data-conversation-region`), и
133
- эта метка теперь в списке исключений. Проверено тестом: подпись рядом с перепиской
134
- превращается в «Нажмите «Сохранить» для продолжения», сообщение остаётся дословным.
135
-
136
- ### Добавлено
137
-
138
- - **Пакет говорит, когда проиграл язык другому пакету.** Сервис локали отдаёт язык тому, кто
139
- зарегистрировал его первым, и второй русский пакет раньше терял свои словари молча.
140
- Теперь в консоли появляется: «[dsh-client-locale-ru] язык ru уже занят другим пакетом; наши
141
- словари не зарегистрированы в 81 namespace. В интерфейсе останется перевод другого пакета —
142
- оставьте в профиле один пакет с русским языком.»
143
- - **Отчёт о плагинах предупреждает о сопернике:** `npm run report:plugins` перечисляет
144
- пакеты, которые сами претендуют на русский, с их версиями.
145
- - **Публикация в CI идемпотентна.** Реестр отвечает на запрос с задержкой в минуты, и релиз,
146
- вырезанный вручную и помеченный тегом, попадал в это окно: страж успевал решить, что
147
- версии нет, а npm отказывался публиковать поверх существующей. Теперь страж спрашивает
148
- трижды, а публикация после отказа перепроверяет реестр и заканчивается успешно.
149
- - **Гейт формы workflow.** Сломанный workflow не падает громко: GitHub сообщает о прогоне по
150
- имени файла, без единого job, а локальные гейты остаются зелёными. `npm run
151
- check:workflow-yaml` проверяет отступы, `run:`/`uses:` у каждого шага и табуляции.
152
-
153
- Цепочка verify выросла до 22 проверок.
154
-
155
- ## [0.2.6] — 2026-10-19
156
-
157
- ### Изменено
158
-
159
- - **Описание пакета теперь содержит слова, по которым его ищут.** Замер поиска npm: пакет был
160
- первым по запросу «dsh language pack», но не попадал в первые 250 результатов по «dsh i18n»
161
- и «dsh russian» — у соседнего пакета эти слова есть в *имени* (`dsh-i18n`), а мы можем
162
- совпасть только текстом. В описании появились «i18n» и «Russian (ru)», а в блок метаданных
163
- каталога — «i18n».
164
- - **В трёх README описана причина отказа установки.** Политика зрелости pnpm не ставит версии,
165
- опубликованные меньше 48 часов назад: профиль отвечает `ERR_PNPM_NO_MATURE_MATCHING_VERSION`,
166
- и это выглядит как поломка пакета. Теперь в разделе установки сказано, что это политика и
167
- что с ней делать.
168
-
169
- ## [0.2.5] — 2026-10-19
170
-
171
- ### Исправлено
172
-
173
- - **Workflow перестал быть нечитаемым для GitHub.** Шаг в обоих workflow был вставлен с
174
- отступом `run:` вместо отступа шага, поэтому файл не разбирался: GitHub сообщал о прогоне
175
- по имени файла, без единого job, а локальные гейты оставались зелёными. Отступ исправлен,
176
- оба файла разбираются настоящим YAML-парсером.
177
- - **Добавлен гейт `npm run check:workflow-yaml`:** заголовок, раздел `on:`, раздел `jobs`,
178
- все шаги на одном уровне отступа, у каждого шага есть `run:` или `uses:`, никаких
179
- табуляций. Проверен инъекцией — сдвинутый отступ шага валит гейт. Гейт вошёл в цепочку
180
- verify (19 проверок) и в оба workflow.
181
-
182
- Содержательно пакет не менялся: 81 namespace, 4949 строк. Выпуск нужен, чтобы тег, дерево
183
- и реестр описывали одно и то же состояние.
184
-
185
- ## [0.2.4] — 2026-10-19
186
-
187
- ### Исправлено
188
-
189
- - **Версия в блоке метаданных каталога** была на два выпуска позади: блок
190
- `deepseek-harness-meta` в трёх README проверялся только релизным пайплайном, и локальные
191
- гейты его не видели. Теперь проверка `npm run check:meta` стоит в цепочке verify и в обоих
192
- workflow, а версия и заявленное число строк в блоке приведены к пакету.
193
- - Выпуск 0.2.3 остаётся в реестре с устаревшей строкой версии в блоке: снять его нельзя —
194
- granular-токен npm не выполняет unpublish. Каталог читает последнюю версию, поэтому
195
- правильные метаданные — здесь.
196
-
197
- ## [0.2.3] — 2026-10-19
198
-
199
- ### Добавлено
200
-
201
- - **Три чужих словаря, которые мы теперь тоже переводим.** Замер по реестру (поиск «dsh»,
202
- 60 пакетов с манифестом `dsh`) показал: словари есть у 10 пакетов, мы покрывали 873 ключа
203
- из 1417. Теперь закрыты все оставшиеся namespace:
204
- - `dsh-remote-ssh` (@zhangfengshun/dsh-remote-ssh@2.4.33, MIT) — 111 ключей: профили
205
- подключения, импорт из ~/.ssh/config, удалённые файлы, терминал, синхронизация;
206
- - `univer` (dsh-univer-office@0.3.7, Apache-2.0) — 49 ключей: живой просмотр документов,
207
- слияние и отклонение изменений, карточки проверки в разговоре;
208
- - `web-ui-plugins` (@linxin666/dsh-client-ui-web-ui-settings@0.4.7, Apache-2.0) — 1 ключ.
209
- - **Покрытие экосистемы: 1327 ключей из 1417 (94 %)** — против 873 (62 %) месяц назад.
210
-
211
- ### Изменено
212
-
213
- - **Подписи времени в codexUi** («{count} дн», «{count} нед») не склоняют существительное и
214
- требовали форм числа, которых у плагина нет. Заменены нейтральной формулировкой
215
- («дней: {count}», «недель: {count}»).
216
- - **Семь имён собственных** Remote-SSH (glob, grep, node, sshpass, keyPath, nodePath,
217
- remoteRoot) добавлены в список латиницы с указанием происхождения; «issue #23» переведено
218
- в «проблема №23».
219
-
220
- ### Исправлено
221
-
222
- - `picker.errNameBad`: литералы «.» и «..» взяты в кавычки — пробел перед точкой был
223
- дефектом типографики, а без них шаблон требовал закончить фразу двумя точками.
224
-
225
- Инвентарь и заявленные числа обновлены: **81 namespace, 4949 строк**, пакет 493 КБ.
226
-
227
- ## [0.2.2] — 2026-10-18
228
-
229
- ### Изменено
230
-
231
- - **Метаданные пакета переписаны под поиск.** Замер по реестру: по запросам «dsh language
232
- pack», «dsh i18n», «dsh russian», «dsh locale» пакет не входил в первые 25 результатов, а
233
- соседний языковой пакет стоял на первом-втором месте. Описание начиналось со слов, которых
234
- в запросах нет, и не содержало фразы `dsh language pack`.
235
- Теперь описание начинается с `dsh language pack — Russian (ru) locale…`, а ключевые слова
236
- добавлены в трёх видах: служебные (`dsh-language-pack`, `dsh-locale`, `dsh-i18n`,
237
- `dsh-russian`), человеческие (`language-pack`, `locale`, `russian`) и русские (`русский`,
238
- `перевод`, `локализация`) — последние нашлись у соседнего пакета, и по ним он ранжировался
239
- выше нас.
240
- Код, словари и бандл не менялись: это выпуск метаданных.
241
-
242
- ## [0.2.1] — 2026-10-14
243
-
244
- ### Добавлено
245
-
246
- - **Новая возможность: `npm run report:plugins`.** Отчёт об английском в собственной
247
- установке: обходит `node_modules` профиля, находит в каждом плагине его словарь и
248
- сравнивает ключи с нашим. Плагин без словаря называется так и есть — переводить нечего,
249
- пока плагин не спросит перевод; плагин, который уже несёт свой русский, помечается
250
- отдельно; «ждать нашей работы» считается отдельным числом, потому что это и есть
251
- очередь. Отчёт читает и `--json`.
252
- На живой установке он сразу дал результат: из девяти DSH-плагинов **семь не имеют
253
- словаря вовсе** (`@cloudbase/dsh-plugin`, `@goodandready/dsh-live-canvas`,
254
- `@local/dsh-quota-widget`, `dsh-figma-mcp`, `dsh-workbuddy-connect`, `superdesign-dsh`),
255
- два наших (`@ragnoryok1/dsh-client-theme-pack` — 41 строка, `@ragnoryok1/dsh-plugin-doctor`
256
- — 45 строк) уже двуязычные, и **ни один плагин не остался непокрытым**. Это и есть ответ
257
- на вопрос «стоит ли переводить соседей»: переводить там нечего, пока они не начнут
258
- спрашивать перевод.
259
- - **Три слепых пятна разбора закрыты, и все три — в собственных пакетах.** Скрипт выгрузки
260
- понимал только `locale.register(NS, { zh, en })`. Настоящий контракт допускает ещё три
261
- формы: `register(NS, "en", en)` с словарём по имени (так устроен наш `plugin-doctor`),
262
- `register(NS, { en: {…}, ru: {…} })` и `register(NS, { en, ru })` с обеими половинами по
263
- имени (так устроен наш `theme-pack`). Пока правило знало одну форму, три собственных
264
- плагина выглядели как «словаря нет» — ровно тот класс дефекта, что и дефис в ключе в
265
- круге 5.
266
- - **Тринадцатый словарь: `task-board-github` (113 строк).** Синхронизация задач GitHub:
267
- настройка репозиториев и метки включения, учётные данные (токен уходит один раз на хост и
268
- в браузер не возвращается), фоновая синхронизация с сообщениями о том, что она
269
- остановилась, карточка PR с состояниями, «промпт выполнения» с шаблоном и неизменяемым
270
- блоком происхождения, анализ задачи моделью и предупреждение, когда задача не требует
271
- правок кода. Пакет вырос до **77 namespace и 4495 строк**.
272
- - **Границы фронта выяснены: у конкурента 13 выгружаемых словарей, а не 16.**
273
- `session-id` (0.1.1) вообще не содержит словаря — его текст зашит в бандл, `t(...)` там
274
- нет, поэтому переводить в нём нечего; `settings.pluginManager` — это namespace **самого
275
- харнесса**, и у нас он был переведён задолго до наступления. Оставшийся
276
- `@linxin666/dsh-client-ui-plugin-manager` тоже пишет в настройки харнесса, поэтому своего
277
- словаря не имеет.
278
- - **Двенадцатый и самый крупный словарь: `task-board` (437 строк).** Доска задач: колонки
279
- и группировка по времени, карточки с подзадачами и расписаниями cron, диалог создания с
280
- разбором текста моделью, метки с подсказками для запуска, пакет передачи, замороженный
281
- снимок, выполнение командой агентов и встроенным `/goal`, а также **отчёт о приёмке**:
282
- критерии качества, порог, раунды A/B, недопустимые вердикты с причинами, аномалии,
283
- очистка доказательств. Пакет вырос до **76 namespace и 4382 строк**.
284
- Итог фронта: **все двенадцать выгруженных словарей семейства переведены** — 1349 строк,
285
- у конкурента закрыто 12 namespace из 16. Проверки пополнились тремя решениями: имя
286
- встроенного пресета `Cordis` объявлено в `keep-english.json`, `issue` в прозе заменён
287
- русской формулировкой (это чужое слово для трекера), а `goalRun` — имя поля настройки, и
288
- вместо него в тексте теперь говорится о флаге запуска через `/goal`. Собственная
289
- неряшливость тоже найдена: «Не более {count} запусков» ломалось на единице, стало
290
- «Предел запусков: {count}».
291
- - **Одиннадцатый словарь: `dsh-ssh` (154 строки).** Панель удалённых операций:
292
- хосты и группы, импорт из `~/.ssh/config` с причинами пропуска (блок `Match`, шаблон с
293
- подстановкой, уже настроенный псевдоним), аутентификация ключом, агентом и паролем,
294
- проверка подключения с задержкой, терминал с двухфакторной аутентификацией, передача
295
- файлов и туннели. Пакет вырос до **75 namespace и 3945 строк**.
296
- Проверки получили две записи в политику: имена потоков `Stdout`/`Stderr`, аббревиатура
297
- `SSH` и директива `ProxyCommand` объявлены в `keep-english.json` (это литералы, а не
298
- непереведённый текст), а `pageant`, `development`, `production` и `none` — разрешённые
299
- латинские значения полей. Заодно убраны три собственные неряшливости: «{bytes} байт»
300
- переписано как «Готово, байт: {bytes}» (иначе на единице получалось «1 байт» не по
301
- форме), «блок Match» получил заглавную по шаблону, а адресная форма
302
- `[user@]host[:port]` заменена русской формулировкой — англоязычный шаблон в подсказке
303
- читался как непереведённый текст.
304
- - **Девятый и десятый словари: `liangshen` (42 строки) и `dsh-web-ui-preset-center`
305
- (46 строк).** Первый — режим LiangShen: рычаг, три состояния, предупреждения о том, что
306
- режим фиксируется после старта сессии, роль диспетчера и представление инструментов в
307
- запросе. Второй — центр пресетов: установка, включение, обновление, удаление, состояния
308
- («id занят пресетом другого плагина», «изменён локально»), предупреждение о том, что
309
- установка исполняемого пресета равна доступу к командной строке. Пакет вырос до
310
- **74 namespace и 3791 строки**.
311
- Здесь же три уточнения проверок: `settings.*` в чужих словарях сверяются по шаблону
312
- **своего** namespace (иначе диалог «Установка исполняемого пресета» сравнивался с
313
- вопросом `common · confirm.title` «Enable Full access?» и выглядел как потерянный
314
- вопросительный знак); правило регистра стало односторонним — заглавная в шаблоне не
315
- должна исчезать, а обратное нормально, потому что русское предложение начинается с
316
- заглавной, даже если английское открывается строчным код-токеном
317
- (`customSkillDirs config`, `v{version} release notes`); значение настройки `native`
318
- объявлено в `keep-english.json` — переводить его нельзя, плагин сравнивает его как
319
- литерал, поэтому в прозе оно заменено на «родной».
320
- - **Восьмой и последний выгруженный словарь семейства: `dsh-web-ui-market`.** 106 строк
321
- (`@linxin666/dsh-client-ui-market@0.4.7`) — мастерская: категории и подкатегории, скины,
322
- питомцы, пресеты, установка одним нажатием, «выбор редакции», голоса и npm-загрузки.
323
- Здесь особенно важна сборка из частей: `settings.descriptionPrefix` и `…Suffix`
324
- склеиваются вокруг адреса сайта, поэтому по-русски порядок слов другой, а пробелы на
325
- краях сохранены — иначе на стыке получилось бы «на и ставьте». Пакет вырос до
326
- **72 namespace и 3703 строк**. Имя сайта `dsh-market.com` объявлено в
327
- `keep-english.json`: это адрес, а не непереведённый текст.
328
- Итог по фронту: **все восемь выгруженных словарей семейства переведены** (564 строки до
329
- этого круга и 106 в нём — 670), у конкурента закрыто 8 namespace из 16; остальные его
330
- плагины ещё не выгружены.
331
- - **Седьмой словарь за пределами харнесса: `dsh-skill-explorer`.** 75 строк
332
- (`@linxin666/dsh-client-ui-skill-explorer@0.4.7`, лицензия BSD-3-Clause — первая не
333
- Apache в семействе) — центр навыков: список по группам (системные, проектные,
334
- пользовательские, зарегистрированные во время работы), включение и выключение вызова
335
- моделью, правка и удаление в `.trash`, создание навыка. Пакет вырос до **71 namespace и
336
- 3597 строк**. Единственное новое слово в проверке латиницы — `customSkillDirs`, имя
337
- параметра конфигурации, разрешено как имя параметра.
338
- - **Шестой словарь за пределами харнесса: `remote`.** 115 строк
339
- (`@linxin666/dsh-remote-web-ui@0.4.7`) — удалённый доступ: сопряжение телефона по QR,
340
- публичный туннель Cloudflare, ретрансляция с постоянным именем хоста, доступ по локальной
341
- сети с правилом брандмауэра, cookie устройства, отзыв сопряжений. Самый технический из
342
- словарей семейства: половина строк — предупреждения о том, что канал /api открыт, что
343
- привязка к 0.0.0.0 требует перезапуска и что за обратным прокси сопрягать надо токеном.
344
- Пакет вырос до **70 namespace и 3522 строк**.
345
- Проверка латиницы получила два уточнения: флаг (`--host`, `--token`) и слова внутри
346
- скобок с именем команды (`(cloudflared tunnel run --token)`) — это инструкция, а не
347
- проза; список имён команд закрытый, поэтому `tunnel` в обычном предложении по-прежнему
348
- нарушение. Заодно `latinInValues` теперь спрашивается раньше структурных исключений:
349
- иначе разрешение, чьё слово стало покрываться правилом команд, отчитывалось как
350
- устаревшее, продолжая быть причиной разрешения. `cookie` разрешён как термин HTTP.
351
- - **Пятый словарь за пределами харнесса: `pet`.** 81 строка (`@linxin666/dsh-pet@0.4.7`) —
352
- питомец-компаньон: кормление, игра, магазин лакомств, ранги привязанности, Live2D и
353
- форма настроек. Пакет вырос до **69 namespace и 3407 строк**.
354
- Выгрузка вскрыла ещё три слепых пятна собственного разбора: `String#search` возвращает
355
- `-1`, а не `null`, поэтому запасной путь для `var en =` не срабатывал и целых три
356
- пакета семейства считались «без словаря»; namespace брался из любого `.register(` и один
357
- раз оказался `market-like` (действие Turnstile, а не locale) — теперь он читается из
358
- `locale.register(КОНСТАНТА, { zh, en })` с разрешением константы; наконец `\w` в JS —
359
- ASCII-only, поэтому девять ключей с иероглифами (`pet.rank.name.伙伴`) вообще не
360
- проверялись. После третьей правки сразу нашлись значения, которые до проверок не
361
- доходили.
362
- - **Четвёртый и самый большой словарь за пределами харнесса: `dsh-web-ui-session-archive`.**
363
- 124 строки (`@linxin666/dsh-session-archive@0.4.7`) — менеджер архива сессий: автоархивация
364
- и автоудаление с порогами, пакетные операции, предпросмотр, фильтры, сортировка, защита
365
- запущенных сессий и «семейств». Пакет вырос до **68 namespace и 3326 строк**.
366
- На этом словаре окупилась работа над разбором: **девять ключей с дефисом**
367
- (`arch.issue.no-archive-time`, `arch.reason.family-protected`, …) парсер пакета молча
368
- пропускал, поэтому они не проверялись вовсе. После расширения шаблона ключа до `[\w.-]`
369
- нашлись ещё два незамеченных случая в словарях харнесса — путь `userData/keybindings.json`
370
- (объявлен в `keep-english.json`) и `localStorage` в прозе (разрешён как имя API браузера).
371
- Оба класса дефектов были невидимы ровно потому, что ключ не доходил до проверок.
372
- - **Третий словарь за пределами харнесса: `dsh-web-ui-usage`.** 63 строки
373
- (`@linxin666/dsh-usage@0.4.7`) — статистика расхода: баланс, карточка дня, тарифные
374
- планы, пиковые и непиковые часы DeepSeek, счётчики токенов и «китовый юань» (купон,
375
- который плагин рисует за каждый миллион токенов). Числа без согласования с
376
- существительным, потому что ключи одиночные: «Запросов: {n}», «Китовых юаней
377
- начислено: {minted}». Пакет вырос до **67 namespace и 3202 строк**.
378
- - **Второй словарь за пределами харнесса: `git-graph`.** 63 строки
379
- (`@linxin666/dsh-client-ui-git-graph@0.4.7`) — граф коммитов, создание и переключение
380
- веток, дополнительные рабочие деревья: диалоги, ошибки Git («отслеживаемые файлы были бы
381
- перезаписаны»), относительное время («5 мин назад»), тосты. Пакет вырос до **66
382
- namespace и 3139 строк**; `update` и `git-graph` выгружены из собственных публикаций
383
- плагинов и проверяются теми же правилами, что и харнесс.
384
- Заодно ужесточена проверка латиницы: токен внутри пути (`$DSH_HOME/worktrees/`,
385
- `feature/git-branch-switcher`) или в угловых скобках (`wt/<name>`) — часть примера,
386
- который читатель вводит, а не английская проза; доказано, что проза в середине фразы
387
- по-прежнему ловится («Ветка repository» → нарушение).
388
- - **Первый словарь за пределами харнесса: `update`.** Пакет переводил только клиент
389
- харнесса и его собственные плагины; сторонние плагины оставались английскими, потому что
390
- их ключей нет в дереве и сверять не с чем. Теперь английский текст берётся из
391
- **собственной публикации плагина**: `npm run fetch:plugins` скачивает пакет, выгружает
392
- `en` из его бандла и записывает `plugins/<namespace>.json` с происхождением
393
- (пакет, версия, лицензия). Аудит подхватывает эти словари и применяет к ним те же
394
- правила: полнота по ключам, плейсхолдеры, типографика, латиница, написание названий.
395
- Первый переведённый — `update` (43 строки, `@linxin666/dsh-update@0.4.7`): панель
396
- обновлений целиком, включая команды pnpm и сообщения об ошибках. Выгружены также
397
- `git-graph` (63), `dsh-web-ui-usage` (63) и `dsh-web-ui-session-archive` (124) — они
398
- лежат в `plugins/` и ждут перевода, их видно в NAMESPACES.md.
399
- Проверки получили три уточнения: мёртвыми не считаются ключи выгруженных плагинов (их
400
- ссылки — в чужом пакете, а не в дереве); в `terminology.json` появился `latinInValues` —
401
- слово, разрешённое только в конкретном значении (`install` и `enable` в команде
402
- `npm install -g pnpm`), и разрешение, которое уже ничего не разрешает, валит сборку;
403
- из `update.error.stale` убраны прямые кавычки вокруг примера `@linxin666/*`, потому что
404
- кавычки-ёлочки в примере конфигурации велели бы вписать ёлочки в YAML.
405
- - **Названия продуктов писались как получится.** «GitHub Desktop» с «Github» читается
406
- разработчиком как опечатка в имени продукта, и появиться она может только у нас: в
407
- английском шаблоне слово написано правильно. Новый гейт сверяет написание латинских
408
- названий с английскими шаблонами харнесса: если харнесс пишет слово с большой буквы
409
- ровно одним способом, наш вариант обязан совпасть. Два предохранителя от шума: слово,
410
- которое харнесс пишет только строчными («cursor», «desktop»), не считается — там наша
411
- заглавная как раз и означает имя продукта; слово, которое харнесс сам пишет двумя
412
- способами, тоже пропускается. Сейчас расхождений 0 из 1497 латинских слов. Доказано
413
- подстановкой: «GitHub Desktop» → «Github Desktop» даёт
414
- «open-in-app · app.github: «Github», а харнесс пишет «GitHub»» и `exit 1`; вернуть —
415
- чисто.
416
- - **Блок команд в README показывал половину инструментов.** В нём было восемь строк из
417
- пятнадцати: `check:namespaces`, `check:tarball`, `check:workflows`, `namespaces`,
418
- `readmes:record` и `diff:harness` появились в последних кругах и не были перечислены —
419
- читатель (и будущий сопровождающий) не мог узнать, что они есть. Блок переписан на трёх
420
- языках: сначала проверки по одной, затем обслуживание отдельной группой (перегенерация
421
- инвентаря, перезапись хешей README, сравнение двух деревьев харнесса). Структура трёх
422
- README при этом не изменилась — гейт структуры и гейт пар прошли.
423
- - **Три перевода отстали от переформулировок харнесса, и ни один гейт этого не видел.** Все
424
- проверки отвечают на вопрос «полон ли пакет для одного дерева»; ни одна не отвечает на
425
- «переформулировал ли харнесс то, что мы уже перевели». Ключ, у которого английский стал
426
- «Could not display feature preview» вместо «Feature preview is unavailable», сохраняет наш
427
- русский и сохраняет зелёный аудит — а читатель видит фразу, которой больше нет. Новый
428
- инструмент `npm run diff:harness -- <старое дерево> <новое>` сравнивает английские
429
- шаблоны двух деревьев и печатает рабочий список: переформулированные ключи вместе с нашим
430
- переводом, новые строки, исчезнувшие строки. Сравнение `dsh-v0.2.1-alpha.3` →
431
- `dsh-v0.2.1-rc.2` нашло **4** переформулировки, и три из них были настоящими ошибками:
432
- `previewUnavailable` читался как «Предпросмотр возможности недоступен», хотя английский
433
- сменился на «не удалось показать» (и «feature» — это «функция», а не «возможность»);
434
- `baseUrlHint` описывал только «оставьте пустым», тогда как английский добавил префикс API
435
- и `/messages`; `maxUsesHint` говорил о «сколько раз запрос может искать», а английский —
436
- о вспомогательной модели и Messages API. Все три переписаны под текущий текст.
437
- Четвёртая переформулировка (`{peers}` → `{requirements}`) была исправлена ещё при
438
- синхронизации.
439
- - **Проверялся репозиторий, а не то, что устанавливают.** Все проверки читают рабочее
440
- дерево: `check:built` сравнивает `lib/` с HEAD, `check:manifest` обходит `exports` на
441
- диске, `check:namespaces` грузит `lib/client.js` из чекаута. Сам архив при этом не
442
- смотрел никто — а пользователь ставит именно его: файл, забытый в `files`, цель
443
- `exports`, существующая только в сборке, или бандл, разошедшийся при упаковке, прошли
444
- бы всё и провалились в единственный важный момент. Новый `check:tarball` пакует
445
- пакет, распаковывает в стороне и задаёт те же вопросы архиву: каждая цель `exports` и
446
- `dsh.bundle.patch` на месте, документация (`NAMESPACES.md`, README обоих языков,
447
- CHANGELOG, LICENSE) едет с пакетом, а распакованный `lib/client.js` через
448
- `namespaces.mjs --check --pack` воспроизводит проверенный инвентарь.
449
- Доказано подстановкой: убрать `NAMESPACES.md` из `files` — «NAMESPACES.md обещан в
450
- files, но в архиве его нет» и `exit 1`; вернуть — «опубликованный артефакт совпадает
451
- с проверенным пакетом». Заодно `namespaces.mjs` получил флаг `--pack`, чтобы вопрос
452
- «что в бандле» можно было задать любому бандлу, а не только чекауту.
453
- - **Выпуск мог уйти с историей предыдущей версии.** `npm publish` не смотрит в
454
- CHANGELOG: страница пакета в реестре показывает тот раздел, который там лежит, и
455
- релиз без раздела для своей версии описывал бы предыдущий. Теперь это гейт
456
- (`check:changelog`, в `verify` и обоих workflow): верхний раздел обязан называть
457
- версию из `package.json`, дата — `YYYY-MM-DD` и не из будущего, в разделе есть хотя
458
- бы один пункт, а версии идут по убыванию (вставка посреди истории видна сразу).
459
- Доказано подстановкой: поднять версию в `package.json` до 0.2.2 при CHANGELOG на
460
- 0.2.1 — «верхний раздел описывает 0.2.1, а package.json — 0.2.2; выпуск ушёл бы со
461
- старой историей» и `exit 1`; вернуть — чисто.
462
- - **Релизный конвейер отставал от цепочки проверок, а его аудит смотрел не на тот
463
- тег.** В `publish.yml` не было трёх новых проверок — `check:namespaces`,
464
- `check:readmes:pairs` и матрицы совместимости, — то есть выпуск мог уйти с устаревшим
465
- инвентарём и разошедшимися README. Хуже: шаг аудита в нём был закреплён на
466
- `dsh-v0.2.1-alpha.3`, хотя пакет уже полгода сверяется с `dsh-v0.2.1-rc.2` — релиз
467
- проверялся бы против чужого дерева. Оба места исправлены, матрица совместимости
468
- добавлена и в `verify.yml` (раньше она жила только в локальном прогоне — в круге 53
469
- шаг не появился из-за CRLF, и я это заметил только сейчас).
470
- - **Новый гейт: workflow запускают то же, что `verify`.** `check:workflows` читает
471
- `package.json` и оба workflow и валит сборку, если проверка из цепочки не запускается
472
- в CI, если CI запускает что-то вне цепочки и вне явного списка CI-исключений
473
- (`build` и `check:compat` — единственные, кому нужен особый режим) и если
474
- `check:compat` не покрывает языки, формы и фразы транзитивно в релизном пути.
475
- Доказано подстановкой: убрать шаг `check:namespaces` из `verify.yml` —
476
- «verify.yml не запускает «check:namespaces» из цепочки verify» и `exit 1`.
477
- - **Новый гейт: закреплённый тег совпадает во всех workflow.** Аудит теперь собирает
478
- `HARNESS_TAG` из каждого файла `.github/workflows/*.yml` и требует равенства с
479
- `verify.yml`. Доказано подстановкой: вернуть в `publish.yml` `alpha.3` —
480
- «publish.yml: HARNESS_TAG dsh-v0.2.1-alpha.3, а закреплён dsh-v0.2.1-rc.2» и `exit 1`.
481
- - **Обещание совместимости проверялось не полностью и не в CI.** `check:compat` скачивает
482
- рантайм локали каждой из 12 заявленных версий и прогоняет против него два набора — языки и
483
- формы числа. Собранные фразы не прогонялись, а ломается именно этот слой: `time.locale` и
484
- категории форм приходят от рантайма, и версия, которая резолвит ключ иначе, выдаёт «Раз в 2
485
- дня» из правильных по отдельности кусков. Третий набор добавлен в матрицу, и она теперь
486
- входит в CI, а не только в локальный прогон: обещание в meta-блоке проверяется на каждом
487
- пуше. Итог: **12 версий × 3 набора**, все зелёные. Доказано подстановкой: испортить шаблон
488
- `frequency.weekly` на «По {weekdays} …» — `actual: 'По Пн, Ср в 09:00 (Europe/Moscow)'`,
489
- `expected: 'Пн, Ср в 09:00 (Europe/Moscow)'` и `exit 1`; вернуть — 6/6.
490
- - **Три README теперь не могут разойтись молча.** `check:readmes` сравнивает структуру
491
- (заголовки, таблицы, картинки, абзацы), и именно поэтому правка круга 49 прошла незаметно:
492
- в русском README стояло «пять случаев», в английском — то же устаревшее число, а скрипт
493
- знал шесть; структура при этом совпадала полностью. Харнесс решает ту же задачу для своей
494
- документации записанным хешем на раздел (`README.i18n.yaml`): правишь одну сторону пары —
495
- запись ломается. Здесь это сделано для трёх языков и с привязкой к позиции раздела
496
- (`scripts/readme-pairs.json`, 15 разделов × 3 языка): структура уже гарантирует одинаковую
497
- форму файлов, поэтому раздел 4 — это «Проверки» и «Checks» одновременно. `npm run
498
- readmes:record` перезаписывает запись после синхронной правки, `npm run check:readmes:pairs`
499
- (в `verify` и CI) валит сборку при расхождении и называет, какой язык изменён, а какой нет.
500
- Доказано подстановкой: добавить слово в заголовок русского README — «раздел 4 «Проверки»:
501
- изменён ru, а en, zh — нет» и `exit 1`; вернуть — чисто. Заодно скрипт не считает
502
- `# комментарий` внутри блока кода за заголовок, а раздел без текста (например, блок мета
503
- до первого заголовка) не хешируется как пустая строка.
504
- - **Подпись не помещалась в боковую панель — это видно на нашем же скриншоте.** На снимке
505
- экрана настроек 0.1.8 пункт называется «Встроенные пл…»: 18 символов «Встроенные плагины»
506
- не влезли туда, где «Built-in plugins» и「内置插件」влезают. Ширину колонки задаёт харнесс, а
507
- не мы, поэтому правка на нашей стороне: подпись пункта стала «Встроенные» (10 символов, тот
508
- же термин, что и в `builtInGroup`), а заголовок страницы остался полным — «Встроенные
509
- плагины», у него место есть. Новый гейт `подписи в боковой панели настроек длиннее 16
510
- символов` держит правило: подпись `nav` в namespace `settings` не может быть длиннее 16
511
- символов. Доказано подстановкой: вернуть «Встроенные плагины» — `settings.plugins · nav —
512
- 18 символов: «Встроенные плагины»` и `exit 1`; вернуть короткую — чисто.
513
- - **Ссылка в README вела на файл, которого не было в публикации.** Инвентарь
514
- `NAMESPACES.md` появился в репозитории в круге 45 и на него ссылаются все три README, но в
515
- `package.json` `files` его не было: npm показывает README на странице пакета, и читатель
516
- опубликованной страницы уходил по ссылке в файл, которого в архиве нет. Теперь в архив
517
- попадают и `NAMESPACES.md`, и языковые варианты README (16 файлов вместо 15, 409 КБ), а
518
- `check:manifest` проверяет это правило: каждая относительная ссылка из README должна
519
- существовать в репозитории **и** покрываться `files`. Доказано подстановкой: убрать
520
- `NAMESPACES.md` из `files` — три ошибки («ссылка на NAMESPACES.md, которого нет в
521
- package.json files — он не попадёт в публикацию») и `exit 1`; вернуть — чисто. Заодно
522
- проверяется, что обещанный в README размер и число файлов совпадают с архивом.
523
- - **Документация перестала обещать числа, которые стареют.** README говорил «`check:plural`
524
- — пять случаев», хотя случаев шесть: круг 44 добавил проверку чужого языкового пакета, а
525
- фраза осталась. Ещё одна фраза утверждала «гейтом проверяются шесть групп проверок» — к
526
- этому моменту групп стало больше двадцати (полнота, формы числа, плейсхолдеры, остатки,
527
- терминология, латиница, гигиена, краевые пробелы, стиль, типографика значений, «ё»,
528
- составные фразы, заявленные числа). Оба места переписаны так, что перечисляют состав, а не
529
- количество: «на счетах 0, 1, 2, 3, 5 и 22, на ключе без пары, на дробном числе и на чужом
530
- языковом пакете» и полный список гейтов. Число «18 правил типографики», «6 проверок
531
- сосуществования словарей» и «jsdom, шесть случаев» сверены со скриптами и верны — они и
532
- остались числами. Заодно убрано случайное одинокое слово «групп» на отдельной строке,
533
- оставшееся от прошлой правки.
534
- - **Одно слово — одно написание «ё».** Пакет, где рядом стоят «ещё» и «еще», читается как
535
- машинный перевод, хотя оба варианта по отдельности допустимы. Новая проверка сводит «ё» к
536
- «е» и валит сборку, если одна форма пишется двумя способами; сейчас таких пар 0 из 2494
537
- разных слов. Настоящие разные слова («все» и «всё») объявлены в `scripts/yo-pairs.json`, а
538
- запись, чьи написания из пакета ушли, тоже валит сборку — иначе список решений тихо
539
- расширял бы проверку. Доказано подстановкой: «Ещё» → «Еще» в одном ключе дало
540
- «еще: еще×1, ещё×49» и `exit 1`; выдуманная пара в `yo-pairs.json` — «объявления
541
- yo-pairs.json, которых больше нет в паке: 1» и `exit 1`; обе правки откачены.
542
- - **Словари плагинов впервые прочитаны как текст.** Пока аудит не видел их namespace, 372
543
- строки не проверял никто, кроме меня глазами при переводе; теперь гейты их видят, и
544
- механических находок не было — но чтение всё равно дало шесть правок. `press.stale` в
545
- `claude-code-mods` переводил «drawing» как «рисунок» («Эта кнопка относилась к предыдущему
546
- рисунку») — в интерфейсе это отрисовка: «Эта кнопка осталась от прошлой отрисовки; полоса
547
- обновилась». `press.failed` переставлен в естественный порядок: «Кнопка не сработала».
548
- В `session-inspector` «Наведите в основном чате» стало «Наведите курсор в основном чате» —
549
- без курсора фраза читается как «наведите что-то». В `voice-input` «Заложите около 4 ГБ»
550
- заменено на «Планируйте около 4 ГБ» (закладывают деньги, а не место), «в текущую позицию
551
- курсора» сокращено до «в позицию курсора», а «Попробуйте записать короче» — на «Сделайте её
552
- короче»: ограничение службы не про попытку.
553
- - **Аудит нашёл настоящую причину «мёртвых» namespace — и она была в его собственных
554
- шаблонах.** Идентификатор namespace записывается в кавычках, и три регулярных выражения
555
- ловили его как `[\w.]+` — без дефиса. Поэтому `open-in-app`, `voice-input`,
556
- `session-log-download`, `session-inspector`, `claude-code-mods`, `agent-team` и
557
- `deepseek-model-label-1017` не связывались со своими словарями: аудит видел имя, но не
558
- ключи. После правки харнесс описывается **67 namespace** вместо 59 (это пакеты самого
559
- дерева — плагины из `client/`, `experimental/`, `session-query/`), и все семь словарей
560
- проверяются как обычные: привязка, ключи, плейсхолдеры, остатки.
561
- - **Привязка namespace к словарю теперь идёт через импорт.** `locales.ts` экспортирует
562
- `NS = 'open-in-app'` рядом со своими блоками `zh`/`en`, а `index.ts` делает
563
- `locale.register(NS, { zh, en })` — имя приходит из другого файла, и правило «константа и
564
- регистрация в одном файле» не срабатывало. Теперь идентификатор разрешается через импорт;
565
- заодно обрабатывается литеральная форма `locale.register('settings.permission', { zh, en })`,
566
- которая раньше объявляла namespace, но ничего к нему не привязывала.
567
- - **Убрано скрещивание словарей.** Модуль, владеющий двумя namespace
568
- (`ui-schedule/index.ts` — `schedule.catalog` и `schedule.manager`), привязывал **каждый**
569
- импортированный словарь к **каждому** своему namespace: 127 ключей менеджера попадали в
570
- каталог и выглядели как дыра в переводе. Привязка по импорту включается только при одном
571
- владельце — когда двусмысленности нет.
572
- - **Namespace-константа считается локалью только там, где её потребляет локаль.** Правило «имя
573
- содержит `NS`» ловило и служебные идентификаторы: `agent-preset-registry` — это хранилище
574
- настроек, `llm-pi-ai` — форма конфигурации провайдера, `session-log-deepseek` — канал
575
- remote. Все три требовали переводов, которые уже есть в своих настоящих namespace (166
576
- ключей). Теперь константа объявляет namespace, только если рядом есть
577
- `locale.register(IDENT…)` или `locale.bind(IDENT)`.
578
- - **Политика `scripts/plugin-namespaces.json` удалена.** Она была обходным путём: пока сканер
579
- не видел дефис, приходилось объявлять плагинные словари вручную и освобождать их от сверки
580
- с шаблонами. Теперь они — обычные namespace харнесса со своими шаблонами, и 31 «английская
581
- копия» в них (названия приложений вроде «Cursor», «VS Code Insiders», служба «Hugging
582
- Face») объявляется так же, как всё остальное: записью в `keep-english.json`, включая
583
- закрытый список `open-in-app · app.*`.
584
- - **Аудит годами не видел семь namespace — и это были все словари плагинов.** Регулярное
585
- выражение, которым аудит вырезал заголовки namespace из `dicts.ts`, было записано как
586
- `[\w.]+`, а дефис в него не входит: `agent-team`, `voice-input`, `claude-code-mods`,
587
- `session-inspector`, `session-log-download`, `open-in-app` и `deepseek-model-label-1017`
588
- молча выпадали из **всех** проверок — 372 строки (12% пакета) не сверялись ни с шаблонами,
589
- ни на плейсхолдеры, ни на остатки, а заявленные числа (57 namespace, 2661 строка) были
590
- меньше того, что пакет на самом деле регистрирует: **64 namespace, 3033 строки**. Проверено
591
- подстановкой: дефект, добавленный в `agent-team` (лишние пробелы и кавычки), поймался
592
- гигиеной — значит правила по тексту работали, а по namespace-привязке нет.
593
- - **Словари плагинов объявлены, а не угадываются.** Харнесс эти namespace не объявляет,
594
- поэтому сверка «мёртвых» считала их мусором, а сверка остатков сравнивала их значения с
595
- чужими шаблонами по одному имени ключа (`title`, `close`, `cancel` встречаются в сотне
596
- namespace) — оттуда 32 ложных «английских копии». Теперь есть
597
- `scripts/plugin-namespaces.json`: объявленный словарь плагина проходит обе проверки, а
598
- необъявленный по-прежнему валит сборку, поэтому опечатка в имени не проедет. Для словарей
599
- плагинов осталось одно правило, которое не требует шаблона: фраза из трёх и более латинских
600
- слов без кириллицы — это непереведённый текст, а односложные названия («Cursor», «Finder»,
601
- «VS Code Insiders») остаются как есть и объявляются решением.
602
- - **Переезды ключей между namespace тоже ловятся.** Записи политики `chat · app.*` и
603
- `pluginManager · sourceHuggingFace` стали устаревшими не потому, что строки перевели, а
604
- потому что харнесс перенёс ключи: приложения переехали в `open-in-app`, `sourceHuggingFace`
605
- — в `voice-input`. Гейт устаревших решений показал обе записи, они переименованы.
606
- - **Инвентарь перевода стал проверяемым документом.** `NAMESPACES.md` генерируется из
607
- собранного бандла (`npm run namespaces`) и проверяется `check:namespaces` в `verify` и CI:
608
- 64 namespace, 3033 строки, у словарей плагинов — пометка. Подпись каждой строки — заголовок
609
- самого namespace, то есть текст, который читает пользователь, а не второе рукописное
610
- описание. Из того же генератора берётся `scripts/pack-count.json` — **один** авторитетный
611
- счётчик для README, бейджа и аудита: раньше аудит считал уникальные имена ключей (2661), а
612
- пакет содержит 3033 значения, и два честных числа выглядели как расхождение.
613
- - **Три строки каталога плагинов перечитаны после перевода.** «Сменить каталог плагинов,
614
- сейчас {market}» — канцелярит с запятой; теперь «Сменить каталог плагинов (сейчас
615
- {market})». «…устанавливать его плагины здесь» — теперь «…устанавливать плагины прямо
616
- здесь». Статус версии «С исключением» читался как канцелярская пометка; теперь «Есть
617
- исключение» — это утверждение о версии, а не о её состоянии.
618
-
619
- - **Пакет догнал харнесс: закреплённая версия поднята с `dsh-v0.2.1-alpha.3` до
620
- `dsh-v0.2.1-rc.2`.** Пока пакет стоял на alpha.3, вышел alpha.4, alpha.5 и два кандидата в
621
- релиз; на rc.2 в клиенте появился новый экран — каталог плагинов (`pluginMarket`, 78
622
- строк: индексы, доверие источникам, карточка плагина, версии, совместимость) — и 30 строк в
623
- существующих namespace: разделы плагинов, смена версии службы (`settings.hostRelease`),
624
- уровни рассуждений (`settings.models`) и новый API веб-поиска (`settings.webSearch`).
625
- Переведены все: покрытие ключей харнесса — **2557 из 2557**, namespace — 57.
626
- Заодно `reasonIncompatibleVersion` переведён под новые плейсхолдеры (`{requirements}` вместо
627
- `{peers}` — харнесс переименовал параметр, и старый перевод потерял бы подстановку).
628
- Диапазон совместимости проверен по-настоящему: `check:peers` и `check:compat` скачивают все
629
- 12 версий из `compatible_versions` и прогоняют оба набора — 12 из 12 проходят.
630
- - **Найден и исправлен ложный namespace в самом аудите.** Сканер искал константы по
631
- подстроке «NS», и `INSTALLED_TAB = 'installed'` (I + NS + TALLED) превратилась в
632
- namespace `installed`, которому аудит приписал 221 ключ из словаря менеджера плагинов. На
633
- alpha.3 этого не было видно: там такой константы не было, а на rc.2 «непокрытый namespace»
634
- с 221 ключом выглядел как огромная дыра в переводе. Теперь «NS» — сегмент имени
635
- (`NS`, `COMMON_NS`, `SETTINGS_NS`), а не случайные буквы: 66 «namespace» стали 59
636
- настоящими. Доказательство: после правки исчез ровно `installed`, а `pluginMarket`
637
- остался — он объявлен как `const NS = 'pluginMarket'` и регистрируется по-настоящему.
638
- - **Переведён и единственный namespace-«заглушка» харнесса** — `deepseek-model-label-1017`
639
- с ключом `badge.label: 1017` (число параметров модели, не текст): без него покрытие было
640
- бы 2556 из 2557.
641
- - **Отчёт полноты по namespace.** Глобальная сверка имён ключей не видит дыру, когда перевод
642
- лежит под чужим namespace: строка считается покрытой, а экран показывает английский. Аудит
643
- теперь восстанавливает связь «namespace → словарь» по тому, кто его регистрирует (`NS` или
644
- литерал в `locale.register`; `bind` и `PropsLocale` только читают namespace) и печатает
645
- такие ключи вместе с английским шаблоном. Отчёт нашёл **49 ключей в 6 namespace**, и все
646
- они разобраны: 45 перенесены из namespace, который обслуживает тот же словарь
647
- (`subagent` ← `settings.subagent`, `schedule.manager` ← `schedule.catalog`, `job` ←
648
- `conversation`), а 4 переведены заново по контексту — `model.search.placeholder`
649
- («Поиск моделей…» вместо общего «Поиск…»), `inspectorPanel.title`/`toggle`
650
- («Инспектор Node.js») и `question.error.unavailable`. На закреплённом теге отчёт теперь
651
- пуст. Он остаётся **справочным**, а не гейтом: связь «namespace → словарь»
652
- восстанавливается по шаблонам регистрации, и эвристика, способная выдумать расхождение,
653
- не должна останавливать выпуск.
654
- - **Гейт остатков: английский, китайский, пустые.** Значение обязано быть русским — аудит
655
- сверяет каждое с английскими шаблонами ключа и валит сборку на точной копии английского,
656
- китайском иероглифе или пустой строке там, где шаблон не пуст. Копии английского
657
- допускаются только по решению из `scripts/keep-english.json` (адреса, форматные строки без
658
- слов, названия протоколов и инструментов, аббревиатуры) — новая недопереведённая строка не
659
- проедет молча. Проверка сразу нашла два упущения: подписи траектории `kind.*` (`SYSTEM`,
660
- `USER`, `ASSISTANT`, `TOOL`, `SUBTOOL`, `Sub`, `CONTEXT`, `COMPACTED` оставались
661
- английскими, хотя китайский словарь их переводит, а `kind.message` уже был русским) и
662
- заголовок окна инспектора `NodeJS Inspector`. Ещё три строки — `Finder`, `Hugging Face` и
663
- `JSON` — оказались в `common`, куда прежняя версия проверки не заглядывала, и объявлены в
664
- политике как названия продуктов и аббревиатуры.
665
- - **Мёртвые ключи удалены, и это теперь гейт.** Ключ, которого нет ни в одном словаре
666
- харнесса и на который не ссылается ни один его исходник, — остаток от прежней версии: он
667
- ничего не показывает и только путает. Таких нашлось 25 — старые `settings.theme.fontSize.*`,
668
- которые харнесс переименовал в `fontSize.text|code|terminal.*`, ключи `pluginManager`,
669
- `settings.agentPreset`, `schedule.catalog.status.*`, `workspace.defaultWorkspace.title`
670
- и `model.option.deepseekV4*`. Все удалены: в пакете **2498 переводов вместо 2523**. Аудит
671
- валит сборку на новом таком ключе, а те, что компонент всё ещё спрашивает, оставляет в
672
- справочном отчёте со счётчиком ссылок: для них наше значение — единственное, что попадает
673
- на экран.
674
- - **Гейт терминологии: одно понятие — один перевод.** Ни одна проверка не видела, что одно и
675
- то же слово переведено двумя способами: обе строки русские и обе на месте. Теперь
676
- запрещённые варианты («кеш» вместо «кэш», «сеанс» вместо «сессия», «этап» вместо «шаг»,
677
- а также «юзер», «ретрай», «апдейт», «сабмит», «дропдаун», «чекбокс» и другие) валят
678
- сборку, а `scripts/terminology.json` хранит и решение, и счётчики выбранных форм. Проверка
679
- сразу нашла два дефекта: «некешированный» в `cotTranslation.paidNotice` и ключ
680
- `confirm.description`, переведённый в `settings.permission` как «шаги подтверждения»,
681
- а в `permission.access` — как «этапы»; там же исправлено «Этапы подготовки» в голосовом
682
- вводе.
683
- - **Латиница внутри русских фраз — под проверкой.** Остатки ловят только значение целиком
684
- на английском, а «компоненты этого bundle» — это середина фразы, и её не видел никто.
685
- Аудит разбирает русские значения на слова и сообщает о строчных латинских словах вне
686
- списка `terminology.json`; в списке 44 записи с причиной — имя продукта, команда,
687
- код-токен, схема адреса, имя настройки, путь в примере. Разбор показал, что реальных
688
- дефектов в пакете нет: вся латиница — это `DSH`, `JSON`, `Bash`, `Esc`, адреса, `pnpm`,
689
- имена слэш-команд и ключи конфигурации. Проверка нужна, чтобы новый английский оборот
690
- в предложении не проехал незамеченным.
691
- - **Короткие слова больше не отрываются переносом.** К экранной типографике добавлено правило:
692
- предлог, союз или частица («в», «и», «с», «на», «не», «ли», «же» и ещё два десятка) держат
693
- следующее слово неразрывным пробелом, поэтому строка не может закончиться на висящем «в».
694
- Слово прикрепляется только к русскому слову — в смешанном тексте «и option» не трогается, —
695
- и цепочка обрабатывается целиком («и в тексте» остаётся неразрывной). Проверок стало 18,
696
- включая случаи «не трогать»: cron-выражение, версии, латиница, части слов. Прогон по всем
697
- значениям пакета: типографика меняет 674 строки из 2839, подозрительных — ноль.
698
- - **Правило числовых диапазонов сознательно не добавлено.** Русская типографика пишет
699
- диапазоны средним тире («5–10»), но единственная строка в пакете с дефисом между числами —
700
- пример cron-выражения `0 9 * * 1-5`, и тире его испортило бы. Это измеренный вывод, поэтому
701
- в тестах появился случай, закрепляющий обратное: cron-выражение обязано остаться как есть.
702
- - **Проверка стиля против шаблона своего namespace.** Значение может быть переставлено
703
- по-русски («Напоминаний: {count}» вместо «{count} reminders»), но знак в конце терять
704
- нельзя, а регистр первой буквы обязан совпадать. Проверка сразу нашла `registryCustomHint`
705
- без точки там, где английский её ставит. Два уточнения, без которых она врала: регистр
706
- сверяется только когда шаблон не начинается с плейсхолдера и наше значение не начинается
707
- с сокращения (`JSON параметров` против `parameters JSON` — законная перестановка), а
708
- знаки сверяются в одну сторону — потерянный сообщается, добавленное русское «…» у статуса
709
- «в процессе» нет. Заодно шаблоны декодируются из escape-последовательностей: исходники
710
- пишут `'Creating\u2026'`, и сравнение по сырому тексту считало такое значение кончающимся
711
- на цифру. Нашлась и дыра в самих проверках: файл-агрегат `locales/index.ts` не объявляет
712
- `en`, поэтому весь namespace `common` молча выпадал из сверки стиля и краевых пробелов —
713
- теперь при единственном английском варианте ключа шаблон берётся из общего набора.
714
- - **Дозор за новыми тегами харнесса.** `watch-upstream` раз в неделю сравнивает закреплённый
715
- тег с тегами харнесса: при новом открывает issue со списком шагов (выгрузить дерево,
716
- прогнать аудит, обновить `HARNESS_TAG`, счётчики и CHANGELOG) и красит прогон, чтобы
717
- отставание находилось по расписанию, а не по чужому скриншоту. Сравнение версий проверяется
718
- без сети — `npm run check:upstream`, пять случаев, входит в `verify`; самопроверка сразу
719
- поймала ошибку в компараторе, где релиз `0.2.1` считался старше своего предрелиза
720
- `0.2.1-alpha.3`, и дозор после первого стабильного выпуска срабатывал бы вечно. Заодно
721
- зафиксировано решение про два замка: канонический — `package-lock.json` (CI ставит им),
722
- `pnpm-lock.yaml` объявлен локальным артефактом в `.gitignore`, а не удалён.
723
- - **Русские формы не только у парных ключей.** Харнесс передаёт число и ключам без пары
724
- (`terminal.expand` с `{n}`), и там форма выбиралась одна на все случаи: «Показать ещё 1
725
- строк» вместо «1 строку». Обёртка над `translate` теперь ищет форму вида `<ключ>.<форма>` и
726
- для таких ключей, пакет несёт для них свои `.one` / `.few` / `.many` (три ключа), а счётчик
727
- читается не только из `count`/`n`, но и из первого числового параметра — харнесс называет
728
- счётчики по экрану (`{shown}`, `{turns}`, `{pending}`). Форма подставляется только если она
729
- есть в словаре, поэтому неверно прочитанное число не может породить перевод. Там, где форма
730
- неуместна — в предложении несколько чисел, — значение переписано нейтрально к числу:
731
- «Символов: {total}» вместо «{total} символов», «файлов: {files}» вместо «{files} файлов»,
732
- «Путей: {shown} из {total}», «{bytes} Б» вместо «{bytes} байт», «{pending} в ожидании»
733
- вместо «{pending} ожидает», «Ходов: {turns}, шагов: {steps}». Всего исправлено 13 строк,
734
- аудит получил отчёт, который перечисляет оставшиеся места «число + склоняемое слово»
735
- (с документированным списком нейтральных слов в `terminology.json`), а тестов форм стало
736
- пять — добавлен случай с ключом без пары и с дробным числом.
737
- - **Исправлено: типографика не применялась к тексту, который меняется.** Узел, однажды
738
- обработанный, навсегда помечался готовым, поэтому живые значения — счётчики токенов,
739
- таймеры, прогресс — теряли неразрывный пробел при обновлении: «за 5 ч» превращалось в
740
- «за 4 ч» с обычным пробелом, и число могло оторваться от единицы. Теперь при изменении
741
- текста метка снимается и проход повторяется; цикла нет, потому что запись происходит только
742
- при реальном отличии результата. Дефект закрыт тестом на настоящем DOM (jsdom,
743
- `npm run check:typography:dom`, четыре случая: первый проход, изменение текста, добавленное
744
- поддерево и `code`, остановка при выгрузке). Тест доказан откатом: без исправления он падает
745
- ровно на этом случае — `'за 4 ч'` вместо `'за 4\u00A0ч'`.
746
- - **Подписи скриншотов в README уточнены.** Снимки сделаны в 0.1.8, и сверка строк показала,
747
- что формулировки с тех пор изменились (например, «Влияет только на содержимое беседы» →
748
- «…диалога»); подпись теперь говорит это прямо, чтобы установленный пакет не расходился с
749
- картинкой незаметно.
750
- - **Исправлено: типографика работала при любом языке интерфейса.** Проход включался сразу при
751
- установке пакета, без проверки языка, а правила у него русские: в английском интерфейсе
752
- `"quotes"` превращались в «quotes», а дефис между словами — в длинное тире, то есть пакет
753
- правил текст, который ему не принадлежит. Теперь проход запускается только пока активен
754
- русский и следует за переключением через событие `locale/change` (оно срабатывает на смену
755
- языка и не срабатывает на регистрацию словаря), а проверка языка выполняется перед каждой
756
- правкой, поэтому смена языка действует со следующей отрисовки. Заодно метки обработанных
757
- узлов стали локальными для запуска, а не общими на модуль. Тест на настоящем документе
758
- вырос до шести случаев (чужой язык и переключение языка — новые), тест сосуществования
759
- языков — до семи; доказано откатом: без подписки тест падает на «слушателей 0, ожидался 1»,
760
- без предиката — на неизменённом английском тексте.
761
- - **Аудит читает ещё два вида значений харнесса.** Часть словарей хранит текст
762
- **массивами абзацев** (руководства режимов) или **ссылкой на константу**
763
- (`'hint.plan': PLAN_NEXT_ACTION_EN`), и такие ключи выглядели как «шаблона нет» — а
764
- значит, не сверялись ни плейсхолдеры, ни краевые пробелы, ни стиль. Теперь парсер
765
- разворачивает оба вида: сверка плейсхолдеров выросла с 2565 до **2575 ключей**, и под неё
766
- попали восемь руководств целиком. Рядом появились стражи самой политики: пустой
767
- `keep-english.json` или пустой раздел `terminology.json` теперь валят аудит (`exit 2`)
768
- вместо того, чтобы молча ослабить гейт.
769
- - **Числа, которые README заявляют о пакете, теперь сверяются с пакетом.** Бейдж
770
- (`strings-2%2C535`), описание в meta-блоке и строка в тексте — три написания одного факта,
771
- а публикующий workflow проверял из этого блока только версию. Счётчик разошёлся незаметно:
772
- после добавления форм числа в пакете стало 2535 строк, а README продолжали обещать 2526 —
773
- то есть ровно то число, которое читатель сравнивает между паками. Аудит читает все три
774
- написания во всех трёх README и валит сборку на расхождении (сверка сразу нашла 9 мест).
775
- Проверка структуры README получила и **число абзацев**: абзац, добавленный только в один
776
- язык, дважды проходил незамеченным, потому что заголовки, блоки кода, таблицы и картинки
777
- оставались теми же. Считаются не строки, а абзацы — перевод переносится по-своему, но
778
- пропасть не должен. Доказано удалением абзаца: 70 против 71, `exit 1`.
779
- - **Обещание совместимости проверяется, а не подразумевается.** В meta-блоке перечислены восемь
780
- версий харнесса, с которыми пакет работает, но проверялся только закреплённый тег: API, который
781
- есть в нём, мог отсутствовать в старых. Новый `npm run check:compat` берёт список оттуда,
782
- скачивает рантайм локали каждой версии с npm и прогоняет против него оба набора —
783
- сосуществование языков и формы числа. Все восемь проходят (7 + 5 тестов на каждой версии,
784
- включая 0.1.7-alpha.2). Проверка добавлена в релизный workflow как отдельный шаг: обещание
785
- проверяется в тот момент, когда его дают. Отказы тоже проверены — несуществующая версия даёт
786
- «версии нет в реестре, проверьте compatible_versions в README», а сломанный бандл валит оба
787
- набора на каждой версии.
788
- - **Объявление совместимости проверяется по правилу харнесса.** Харнесс читает наши
789
- `peerDependencies` для `@deepseek-ai/dsh*` не как версии пакетов, а как диапазон **версий
790
- рантайма** (`evaluatePluginCompatibility`, с `includePrerelease`): неудовлетворённый диапазон —
791
- это предупреждение «Plugin … is incompatible with dsh …» и запрос на исключение у пользователя.
792
- Диапазон в манифесте — цепочка окон предрелизов, которую правка может незаметно сузить,
793
- поэтому `npm run check:peers` (входит в `verify`) прогоняет это правило по всем заявленным
794
- версиям. Сегодня проходят все восемь; доказано сужением диапазона до `>=0.2.1-0 <0.3.0-0` —
795
- пять внятных провалов на старых версиях. Заодно в devDependencies добавлен `semver` (им же
796
- пользуется харнесс).
797
- - **Манифест проверяется по правилам харнесса.** Три правила вычитаны из его исходников, и
798
- каждое — отказ, который иначе увидел бы пользователь: `parseDshClient` принимает `platform`
799
- любой строкой, но потребители отбирают только `platform === 'web'`, поэтому опечатка `'Web'`
800
- **молча выключила бы** клиентскую половину; объявленный `dsh.client` без экспорта `./client`
801
- заставляет `client-modules` бросить ошибку (в том числе если файл по этому пути не существует);
802
- `dsh.bundle.patch` обязан указывать на существующий файл. `npm run check:manifest` входит в
803
- `verify` и доказан двумя подстановками: `'Web'` вместо `'web'` и удалённый экспорт — оба
804
- пойманы с объяснением.
805
- - **Разбор формулировок по всему пакету.** Прошёл по строкам с признаками машинного или
806
- канцелярского перевода и поправил два места: «Копировать красивый JSON» → «Копировать
807
- **форматированный** JSON» (калька с `pretty`) и «нетекстовый **контент**» → «нетекстовое
808
- **содержимое**» (в пакете `содержимое` встречается 14 раз, `контент` — только в этих двух).
809
- Заодно закрыт класс, который нашёлся при разборе: обращение к пользователю. Внутри фразы
810
- русский интерфейс пишет «вы» со строчной, а «Вы» с заглавной — только в начале предложения,
811
- где это орфография; проверка смотрит строго на регистр и только на середину фразы.
812
- Любопытно, что первая версия правила была слишком широкой и завалила четыре правильные
813
- строки — «Вы вошли в DeepSeek» в начале фразы корректно, — так что правило уточнено, а не
814
- строки испорчены. Доказано подстановкой: «Ещё и Вы» → «Вы с заглавной в середине фразы».
815
- - **Отчёт о коротких строках, выросших в длине.** Русский длиннее английского по природе
816
- («Command failed» → «Команда завершилась с ошибкой»), поэтому отчёт показывает только
817
- крайние случаи — от 2,8× у коротких шаблонов (до 28 символов, без плейсхолдеров), где
818
- текст упирается в вёрстку кнопки, вкладки или значка. Таких три: «Effort» → «Уровень
819
- рассуждения», «SUBTOOL» → «ИНСТРУМЕНТ СУБАГЕНТА», «Job ID» → «ID фоновой задачи»; все три
820
- оставлены сознательно — это точные термины, а не многословие. Проверено и обратное:
821
- сравнение по первому попавшемуся шаблону давало 92 «нарушения», из которых почти все —
822
- артефакт чужого namespace, поэтому отчёт читает шаблон **своего** namespace.
823
- - **Механическая гигиена текста и краевые пробелы.** Первое — двойные пробелы, пробел перед
824
- знаком препинания, непарные «ёлочки» и скобки, неразрывный пробел внутри значения. Второе
825
- тоньше: значение, которое начинается или кончается пробелом, компонент склеивает с
826
- соседним, поэтому краевые пробелы сверяются с английским шаблоном **своего** namespace.
827
- Проверка сразу нашла два реальных дефекта в `schedule.manager`: у `delete.description` и
828
- `timing.conflict` английский шаблон кончается пробелом, а русский — нет, то есть
829
- приклеенный следом текст слипся бы со словом. Ещё две правки: разделитель тысяч
830
- `number.groupSeparator` стал неразрывным пробелом (числа больше не рвутся по строке), а
831
- «ссылка .tgz» в `installFailureNetworkHost` переписано в «прямая ссылка на .tgz».
832
- - **Проверка полноты покрывает все 54 namespace.** Привязка «namespace → словарь» строилась
833
- только по импорту словаря из того же каталога, поэтому `common` и `settings.locale` — чьи
834
- словари лежат в `../locales/` — оставались вне сверки: 52 из 54. Теперь разбирается и
835
- регистрирующая строка с идентификатором (`locale.register(NS, { zh, en })`), и имена
836
- импортированных словарей, так что каждый namespace пака сверяется со своим. Пробелов при
837
- этом не нашлось, но слепое пятно закрыто — и оно стало видимым: аудит печатает, сколько
838
- namespace сверено, а какие остались без привязки.
839
- - **Настоящие русские формы числа.** Контракт локали не несёт правил плюрализации:
840
- компонент выбирает `count === 1 ? '.one' : '.other'`, а русскому нужны три формы
841
- (1 вызов / 2 вызова / 5 вызовов). Пакет берёт выбор на себя — рядом с `.one`
842
- и `.other` в словарях появились `.few` и `.many` для 17 фраз (фоновые задачи,
843
- субагенты, шаги, вызовы инструментов, сообщения, сессии, участники запуска,
844
- напоминания, единицы работы в диалоге архивации), а обёртка над `translate`
845
- рантайма пересчитывает форму через `Intl.PluralRules('ru')` для любого вызова
846
- с числовым `count` или `n`. Форму решает число, а не ключ, который выбрал компонент:
847
- `t('…toolCalls.one', { count: 2 })` даёт «2 вызова инструментов», а `{ count: 5 }` —
848
- «5 вызовов инструментов». Ключи без новых форм сохраняют прежнее значение `.other`
849
- (нейтральную конструкцию), поэтому регрессия невозможна; выгрузка плагина снимает
850
- обёртку по счётчику владельцев. Проверка — `npm run check:plural`, четыре теста
851
- на настоящем рантайме локали, и она же добавлена в `verify` и оба workflow.
852
- - **Синхронизация с `dsh 0.2.1-alpha.3`**: покрытие — **2457 из 2457** ключей тега,
853
- 55 namespace. Харнесс добавил 28 ключей и один namespace — `secretMask` (пакет
854
- `ui-secret-mask`, показ и скрытие секрета). Наполнены шесть существующих namespace:
855
- `settings.models` (14 строк: вход в аккаунт, подсказки по ключам и переменным окружения,
856
- показ и скрытие секрета), `pluginManager` (8: причины перезапуска и предпросмотр
857
- возможности), `reference` (4: окно «не удалось открыть файл»), `workspace` (3:
858
- копирование ID сессии), `chat` (2: остановка задачи и прерванный ход), `conversation`
859
- (2: размер картинки в строке). Из пака удалены 7 строк, которых в теге больше нет
860
- (`message.accountStopped`, `settings.models.keyStored`, `keyEnvLocked`,
861
- `keyPlaceholderNative` и три ключа `settings.pluginInventory`), а `restartNotice`
862
- и `installDoneRestart` получили `{reason}`: тег теперь дописывает к ним причину
863
- перезапуска, и без плейсхолдера она не показывалась вовсе.
864
- - **Синхронизация с `dsh 0.2.1-alpha.2`**: покрытие — **2429 из 2429** ключей тега,
865
- 54 namespace. Добавлено 84 строки перевода: новый namespace `cotTranslation`
866
- (32 строки — перевод рассуждений и его настройки) и 52 строки в существующие —
867
- настройки шрифтов (20, `settings.theme`), менеджер плагинов (13),
868
- голосовой ввод (8), сворачивание хода работы в чате (4), аргументы команды и
869
- строка команды (2), миграция каталога и внешняя задача субагента (2),
870
- тосты ветвления сессии (2), ошибка последней попытки (1).
871
- - **Аудит получил третий гейт — паритет плейсхолдеров**: русское значение обязано
872
- нести ровно те `{name}`, что и английский шаблон ключа (для пакетов, которые везут
873
- только китайский словарь, шаблоном служит `zh`). Лишний плейсхолдер попадает на экран
874
- буквально, потерянный молча убирает подставленное значение. Проверка прогнана на живых
875
- дефектах: возвращённый `{duration}` в `message.turnProcess.took` валит аудит с кодом 1
876
- и печатает `ru: duration | шаблон: —`, а убранный `{duration}` из `chat.deepDivingFor` —
877
- `ru: — | шаблон: duration`. Сверено 2534 значения, шаблон недоступен у 35.
878
- - **`check:locale` в CI** — шесть проверок сосуществования и выгрузки словарей,
879
- пришедшие с PR #3.
880
- - **Собранный `lib/` в репозитории** — каталог плагинов генерирует команду без
881
- `@`-скоупа (`dsh plugin --profile web add Ragnoryok1/dsh-client-locale-ru`), и pnpm
882
- ставит её как архив GitHub. Проверено, что `prepare` этой задачи не решает: pnpm
883
- отказывается запускать сборочные скрипты git-зависимости
884
- (`ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED`, нужно вносить пакет в `allowBuilds`), то есть
885
- вместо тихой поломки выходит жёсткий отказ. Поэтому `lib/` теперь лежит в git, и
886
- установка из GitHub не требует сборки на стороне пользователя. `npm publish`
887
- по-прежнему пересобирает `lib/` через `prepublishOnly`.
888
- - **Одна английская фраза — один русский текст.** Новый гейт сверяет пакет с самим собой:
889
- если одну и ту же английскую фразу два namespace переводят по-разному, сборка падает.
890
- Сравниваются шаблоны от четырёх слов — короткая подпись законно зависит от контекста
891
- («Open» — это и «Открыть» в меню, и «Открыто» в статусе). Отчёт сразу нашёл шесть таких
892
- мест, и четыре оказались настоящими дефектами: `permission.access` обещал «уменьшать
893
- число шагов подтверждения», а `settings.permission` — «пропускать шаги», причём
894
- английское «subsequent tasks» в первом стало «текущей задачей» (оставлена точная
895
- формулировка, общая для обоих); «deployment» переводился как «сборка» в пяти namespace
896
- и как «развёртывание» в шестом — приведено к решению из `terminology.json` («деплой» →
897
- «развёртывание»), включая строки плагинного менеджера и `settings.models`; сообщения о
898
- пределе областей и о нехватке ширины разошлись формулировками при одинаковом английском
899
- (`command.budget`/`command.width` против `dock.splitPaneDisabled`/`dock.splitPaneNarrow`) —
900
- сведены к одной; у `terminal.expandRest` и `terminal.expandAria` не было форм числа, хотя у
901
- их двойников из плагинного менеджера они есть, — добавлены `.one/.few/.many`, и «… ещё
902
- строк: 3» стало «… ещё 3 строки». Заодно исправлен `createPluginMissing`: английский
903
- говорит «configuration», а в пакете стояло «сборка». Намеренное расхождение объявляется в
904
- `scripts/one-rendering.json` вместе с причиной, устаревшее исключение валит сборку. Гейт
905
- доказан тремя подстановками: расхождение без исключения → `exit 1` с обоими вариантами и
906
- их источниками; исключение, которое ничего не исключает → `exit 1`; расхождение с
907
- объявленным исключением → `exit 0` и «объявлено вручную: 1».
908
- - **Полнота namespace проверяется в обе стороны.** Аудит валил сборку только за namespace
909
- пака, которых нет в харнессе, а обратная дыра не проверялась: namespace, который харнесс
910
- регистрирует и наполняет ключами, а пак не регистрирует вовсе, оставляет свой экран
911
- английским, и покрытие по именам ключей этого не видит — те же ключи уже переведены под
912
- другим namespace. Харнесс регистрирует 63 namespace, пак вёз 55; из лишних восьми один
913
- оказался настоящим: `settings.sessionLog` — переключатель «Upload Session Log» на странице
914
- общих настроек (четыре строки, включая тосты «Preference saved» и «Could not save
915
- preference»). Namespace, найденные сканером регистраций без единого ключа (константы вроде
916
- `EMPTY_RESPONSE` или `normal`), в проверку не попадают: переводить в них нечего. Добавлен
917
- сам namespace (56-й), гейт доказан удалением: без него `exit 1` и строка
918
- `settings.sessionLog — ключей 4, регистрация: …UploadRow.tsx (PropsLocale)`.
919
- - **README-числа с разрядами и заявление о версии сборки.** Сверка заявленных чисел читала
920
- формы «, 2535 переводов», «, 2535 translated strings» и «、2535 条翻译», но не
921
- «2,489 strings» — и именно в этой форме в обоих английском и китайском README-лидах
922
- держалось число, разошедшееся на полсотни строк: шаблон требовал запятую перед числом, а не
923
- внутри него. Теперь разряды читаются, и на подстановке старый текст поймал себя сам
924
- («заявлено 2489 строк, а в пакете 2542»). Рядом появилась сверка фразы «built against dsh
925
- `X`»: она сравнивается с тегом `HARNESS_TAG` из `verify.yml` — тем самым, которым пакет и
926
- проверяется, — потому что проза и workflow расходятся молча, а читатель сравнивает паки по
927
- этой фразе, а не по workflow. Оба README-лида обещали `0.2.1-alpha.2` при закреплённом
928
- `dsh-v0.2.1-alpha.3`; исправлено, и проверка доказана подстановкой обеих частей.
929
- - **Собранный `lib/` сверяется с коммитом.** Установка из GitHub исполняет закоммиченный
930
- `lib/client.js` (pnpm отказывается запускать сборочный скрипт git-зависимости), поэтому
931
- устаревший бандл попадает к пользователю, а не в аудит — аудит читает исходники, а `verify`
932
- просто пересобирал поверх. Теперь `npm run check:built` пересобирает `lib/` и сравнивает
933
- каждый отслеживаемый файл с `HEAD` (переводы строк нормализуются, поэтому CRLF-чекаут и
934
- LF-чекаут сходятся), а незакоммиченный файл сборки тоже считается расхождением. Сборка
935
- детерминирована — два прогона дают одни и те же байты, это и делает сравнение осмысленным.
936
- Проверка доказана подстановкой: правка одного значения в `dicts.ts` без коммита даёт
937
- `exit 1` и «lib/client.js — расходится с HEAD», после откатa — снова `exit 0`.
938
- - **Заявленный размер пакета меряется, а не запоминается.** «Публикуемый пакет занимает
939
- 354 КБ» было написано раньше и с тех пор разошлось с реальностью на 27 КБ: словари выросли,
940
- а число — нет. `npm run check:size` собирает архив (`npm pack --dry-run --json`) и сверяет
941
- заявленные килобайты с настоящими во всех трёх README, с допуском в один килобайт: gzip на
942
- разных сборках Node сжимает чуть иначе, а фраза округлена до целых килобайт. На подстановке
943
- проверка поймала все три README сразу и назвала измеренное значение (381 КБ).
944
- - **Описание в `package.json` — четвёртое место тех же чисел.** Сверка заявлений читала только
945
- README, а `description` в манифесте — это то, что npm показывает в поиске и на странице
946
- пакета; там дольше всех жило «55 namespaces». Теперь манифест читается тем же кодом, и
947
- фраза «verified against dsh X» сверяется с закреплённым тегом наравне с «built against».
948
- Доказано подстановкой: «55 namespaces» и `0.2.1-alpha.2` в описании — `exit 1` с двумя
949
- отдельными сообщениями.
950
- - **CI прогоняет те же гейты, что `npm run verify`.** `verify.yml` вызывал пять проверок
951
- вручную, а `check:readmes`, `check:manifest`, `check:peers`, `check:upstream` не запускал
952
- вовсе — то есть заявления README в CI не проверялись никогда, и зелёный прогон об этом
953
- молчал. Список шагов выровнен по `verify`, добавлены новые `check:built` и `check:size`;
954
- `publish.yml` получил те же шаги (включая сверку собранного бандла перед публикацией).
955
- - **Карта исходников была платформенной — и первый же прогон CI это поймал.** Новый гейт
956
- `check:built` упал на Linux: `lib/client.js.map` расходился с коммитом. Причина не в бандле
957
- (`lib/client.js` и все `.d.ts` совпали байт в байт) — tsdown вкладывает в карту
958
- `sourcesContent` ровно так, как прочитал файлы, то есть с `\r\n` на Windows и с `\n` на
959
- Linux. Нормализация переводов строк в самой проверке этого не лечит: в JSON это Escaped-
960
- последовательность из двух символов, а не настоящий перевод строки. Сборка теперь кончает
961
- шагом `scripts/normalize-map.mjs`, который приводит `sourcesContent` к LF — той форме, в
962
- которой git и хранит исходники; карта снова детерминирована. Гипотеза проверена не на глаз:
963
- копия репозитория с LF-исходниками собирается в те же шесть файлов, что и коммит (до правки
964
- расходилась ровно карта, после — ни один).
965
- - **Плейсхолдеры наших собственных форм числа теперь сверяются.** Сверка сравнивала значение
966
- со шаблоном харнесса, а у форм, которых в харнессе нет, шаблона нет по определению —
967
- то есть 52 значения (все добавленные `.few`/`.many` и `.one` у непарованных ключей) не
968
- проверялись вовсе, и потерянный `{count}` в такой форме никто бы не увидел. Теперь форма
969
- сверяется с шаблоном соседней формы того же слова (`.one`/`.other`) или самого ключа;
970
- проверено, что шаблона не нашлось вовсе у 0 значений. Доказано подстановкой: убранный
971
- `{count}` из `count.live.few` → `exit 1` с «ru: — | соседняя форма: count».
972
- - **Отчёт «нет шаблона» печатал только счётчик.** Число «шаблон недоступен у 49» ничего не
973
- говорило о том, что это за ключи, — а именно список показывает, дыра это парсера или
974
- наша собственная форма. Теперь ключи перечислены; после правки выше список пуст, и это
975
- утверждение проверяемо, а не подразумеваемо.
976
- - **«Cron-выражение» чуть не стало «cron-выражение» — стилевой гейт это остановил.** Показалось,
977
- что в одном namespace одно и то же слово написано по-разному: подпись поля с заглавной, а
978
- подсказка под ним со строчной («Введите cron-выражение из пяти полей»). Правка была неверной:
979
- метка поля — не предложение, и в русском она начинается с заглавной, ровно как английский
980
- шаблон («Cron expression»), а строчная нужна внутри фразы. Проверка регистра первой буквы
981
- сказала это сама (`регистр: у нас "cron-выражение" | шаблон "Cron expression"`), значение
982
- возвращено. Тот же урок, что с «Вы» в начале фразы: сначала проверка, потом правка.
983
- - **Проверено, что чинить нечего.** Прогнал по пакету два класса, обещанных в прошлом круге:
984
- смешанное написание ё и дефис вместо тире внутри значений. Ё — 64 слова, у всех одна форма;
985
- единственное «совпадение» оказалось разными словами («все» и «всё»), то есть проверять
986
- нечего. Дефис вместо тире — 0 значений, тире без пробела — 0. Гейт на это не заводил: он
987
- ловил бы «все/всё» как ложное срабатывание, а дефектов нет.
988
- - **Решения `keep-english.json` больше не гниют.** Файл разрешает оставить английский текст,
989
- и лишняя запись в нём молча расширяет разрешение — тот же риск, что у исключений
990
- `one-rendering.json`. Теперь каждая запись обязана что-то разрешать: значение ключа
991
- совпадает с шаблоном харнесса. Запись, у которой ключа в пакете уже нет, и запись, чьё
992
- значение перевели, — обе валят сборку, с разными сообщениями, чтобы правка была очевидной.
993
- Проверка сразу нашла `settings.models · deepSeekBaseUrl`: английский шаблон обзавёлся
994
- пояснением «(Default to use DEEPSEEK_BASE_URL)», а наше значение осталось голым адресом —
995
- и это правильно, потому что ключ служит подсказкой внутри поля ввода, где пояснение было бы
996
- мусором. Запись убрана, значение оставлено. Доказано двумя подстановками: несуществующий
997
- ключ → «такого ключа в пакете нет», переведённое значение → «значение уже не совпадает с
998
- шаблоном, запись ничего не разрешает».
999
- - **Видимая зона, до которой сверка остатков не достаёт.** Сверка остатков читает только
1000
- блоки `en`, а у части словарей харнесса этот блок не опознаётся — и такие значения не
1001
- проверялись вовсе. Попытка «просто расширить сверку на все шаблоны» дала 50 находок, и все
1002
- пятьдесят оказались названиями приложений и форматными строками: расширять гейт значило бы
1003
- потребовать полсотни новых записей политики ради того, что английским и должно быть.
1004
- Поэтому зона не расширена, а **напечатана**: справочный список из 60 значений — названия
1005
- приложений (`VS Code`, `iTerm2`), форматные строки (`{from}–{to}`, `{percent}%`), метка
1006
- `en`, подписи `Cron {expression}`. Список проверяем глазами и меняется при правках, поэтому
1007
- переведённая фраза в этой зоне не спрячется незаметно; в README это записано прямо, а не
1008
- выдано за полное покрытие.
1009
- - **Зона закрыта как класс: шаблоны в политике и форматные строки по правилу.** Справочный
1010
- список из прошлого круга был признанием, а не решением, поэтому следующим шагом зона стала
1011
- гейтом. Три изменения. Во-первых, запись политики может называть закрытый список:
1012
- `chat · app.*` разрешает тридцать названий приложений одной строкой — словарь имён
1013
- приложений и есть список имён; шаблон обязан называть namespace, иначе одна звёздочка
1014
- разрешила бы всё (проверка на загрузке политики, `exit 2`). Во-вторых, значение без слов
1015
- вне плейсхолдеров (`{from}–{to}`, `{percent}%`, `, `) не требует решения вовсе: проверка
1016
- снимает плейсхолдеры и ищет букву — форматная строка не бывает «непереведённой прозой»,
1017
- потому что прозы в ней нет; восемь записей после этого стали лишними и удалены. В-третьих,
1018
- сверка остатков читает все шаблоны ключа, а не только блок `en` — та самая зона исчезла:
1019
- сверено 2802 значения (было 2579), 68 покрыты решениями, 38 записей, из них одна шаблон.
1020
- - **Пустое значение больше не валит сборку там, где пуст и шаблон.** Расширение сверки на все
1021
- шаблоны сразу поймало `chat · message.stepProcess.sharedPrefix`: у нас пустая строка, а
1022
- «шаблон» — китайское 「已」. Английский шаблон тоже пуст, просто раньше сверка его не видела.
1023
- Правило стало точнее того, что было: пустое значение — ошибка только там, где **ни один**
1024
- шаблон не пуст, иначе русский справедливо опускает префикс, который английский опускает
1025
- тоже. Доказано на живом ключе, а не на подстановке: до правки — `exit 1`, после — `пустые 0`.
1026
- - **Шаблоны политики тоже проверяются на гниль.** Шаблон, под который не подходит ни одно
1027
- значение, требующее решения, теперь валит сборку — иначе одна опечатка в `chat · app.*`
1028
- тихо разрешала бы пустоту. Доказано подстановками: переименованный шаблон
1029
- (`chat · app.qqq*`) → «под шаблон не подходит ни одно значение, которое нужно разрешить»;
1030
- шаблон без namespace → `exit 2`; сломанный шаблон (`chat · app.nope` без звёздочки) →
1031
- «такого ключа в пакете нет» и 31 неразрешённая копия.
1032
- - **Каждой паре форм харнесса нужны русские `.few`/`.many`.** Обёртка подставляет форму
1033
- только если словарь её несёт, иначе молча возвращается к нейтральному `.other` — то есть
1034
- отсутствие формы не падает и не мигает, просто «2 дня» превращается в «2 д». Новый гейт
1035
- сверяет это **по namespace**: обёртка ищет форму в том namespace, который спросил, значит
1036
- пара, забытая на одном экране при наличии на другом, иначе невидима. Нашлись четыре
1037
- единицы времени в `schedule.catalog` (восемь форм) и `trigger` в менеджере задач (две).
1038
- Заодно исправлены сами единицы: они были сокращениями («д», «ч», «мин», «с»), хотя
1039
- подставляются в прозу — «через 2 д», «Раз в 30 мин». Теперь полные формы с правильным
1040
- падежом: день/дня/дней, час/часа/часов, минуту/минуты/минут, секунду/секунды/секунд —
1041
- «через 2 дня», «через 5 часов», «через 1 минуту». Это же сняло согласование в шапке:
1042
- «Каждые {value} {unit}» давало «Каждые 1 день», стало «Раз в {value} {unit}» — верно для
1043
- любого числа и любого из четырёх слов.
1044
- - **Тест форм числа перестал фиксировать дефект.** Кейс проверял, что `unit.day` **не имеет**
1045
- `.few`/`.many` и возвращает «д»: он документировал пробел как норму. Теперь он проверяет
1046
- обратное — «1 день», «2 дня», «5 дней», «3 часа» и «1,5 минуты» для дробного счёта, где
1047
- CLDR не даёт формы и срабатывает именно `.other`.
1048
- - **Значения проверяются на типографику отдельно от DOM.** Проход типографики видит только
1049
- текстовые узлы, а значение из словаря попадает ещё и в атрибуты — `title`, `aria-label`,
1050
- `placeholder`, — до которых он не достаёт. Значит правило «исправлю при отрисовке» для них
1051
- не работало вовсе, и одна и та же строка выглядела бы по-разному в зависимости от места.
1052
- Новый гейт требует, чтобы значения были уже правильными: без прямых кавычек, без дефиса
1053
- вместо тире, без тире без пробела и без трёх точек. Сейчас дефектов 0 — это защита от
1054
- регрессии, а не находка.
1055
- - **Совместимость с чужим русским пакетом — проверено, а не предположено.** В
1056
- `@deepseek-ai/dsh-client-locale` вызов `addLanguage` бросает `locale "ru" is already
1057
- registered`, если `ru` уже занят, — с русским пакетом конкурента это теперь реальный
1058
- сценарий. Наш `apply` ловит **только** это сообщение и только для занятой записи: язык и
1059
- словари первого регистранта остаются, остальные наши namespace регистрируются, а любая
1060
- другая ошибка по-прежнему валит активацию. Поведение закреплено тестами `check:locale`
1061
- («другой пакет первым», «занятый namespace», «наша активация второй», «чужая ошибка не
1062
- проглатывается»); отдельно сверено с кодом конкурента: он глушит **все** ошибки регистрации
1063
- пустым `catch {}`, то есть при обратном порядке установки теряет и настоящие сбои.
1064
- - **Даты и часовые пояса теперь по-русски — и это закреплено тестом.** Тот же тег `time.locale`
1065
- печатает отметки времени: с `en` русский читатель видел «Oct 5, 2026, 3:51 PM». `check:composition`
1066
- получил шестой случай — он берёт наборы опций из `formatScheduleAbsolute` и
1067
- `formatScheduleNextRun`, форматирует фиксированный момент и требует кириллический месяц
1068
- («5 окт. 2026 г., 15:51») и отсутствие латинских слов, а от подписи пояса — «Москва» при
1069
- сохранённом ASCII-префиксе `UTC`. Строки сравниваются по признакам, а не по точному
1070
- равенству: ICU в разных сборках Node расставляет пробелы и сокращения по-своему, и тест не
1071
- должен падать от чужой версии `Intl`. Доказано подстановкой: `time.locale` = `en` →
1072
- `actual: 'Oct 5, 2026, 15:51'` против `/окт/`, `exit 1`.
1073
- - **Описание cron переведено заново: счёт — через формы числа, остальное — без книжных
1074
- оборотов.** В `cron.time.*` стояли сокращения («Каждые {step} мин», «Каждые {count} ч») и
1075
- кальки («Каждый час в минуту {minutes}», «минуты {minutes} в часы {hours}», «каждую минуту в
1076
- часы {hours}»). Счётные ключи получили `.few`/`.many`: «Каждые 3 минуты» / «Каждые 5 минут»,
1077
- «Каждые 2 часа» / «Каждые 6 часов» — обёртка подставляет их сама, потому что `{step}` и
1078
- `{count}` — первые числовые параметры вызова. Остальные переписаны в один читаемый вид с
1079
- метками: «Каждый час, минуты: 5, 30», «каждую минуту, часы: 09–17», «каждые 5 минут, часы:
1080
- 09–17», «минуты: 5, часы: 09–17». `check:composition` получил пятый случай, который
1081
- складывает фразу расписания целиком — «Пн–Пт каждые 5 минут» — и проверяет формы через
1082
- настоящий `translate`. Доказано подстановкой: возвращённое «Каждые {count} ч» → `actual:
1083
- 'Каждые 2 ч'` против ожидаемого `'Каждые 2 часа'`, `exit 1`.
1084
- - **«Каждый день в January» — `time.locale` был не текстом, а тегом `Intl`.** Харнесс строит
1085
- названия месяцев и подписи часовых поясов через `Intl.DateTimeFormat(t('time.locale'))` и
1086
- передаёт тот же тег в разбор cron. Английский словарь харнесса держит там `en`, китайский —
1087
- `zh-CN`, а наш пакет унаследовал `en`: правило с ограничением по месяцу показывало
1088
- «Каждый день в January», а подпись пояса — «Moscow Standard Time» в русском интерфейсе.
1089
- Теперь тег `ru` (оба namespace), а `cron.months` переписан под именительный падеж, который
1090
- даёт `Intl`: « в {months}» → « ({months})», то есть «Каждый день (январь, март)» вместо
1091
- невозможного «в январь». Записи политики `keep-english.json` про `time.locale`
1092
- («код языка, который подставляет браузер») после этого перестали что-либо разрешать —
1093
- гейт устаревших решений указал на обе, и они удалены: 36 записей вместо 38.
1094
- - **`check:composition` получил четвёртый случай** — правило с ограничением по месяцу: тег
1095
- обязан быть `ru`, месяцы приходят из `Intl` по-русски («январь, март»), фраза целиком —
1096
- «Каждый день (январь, март)», и в ней не остаётся ни одного латинского слова. Доказано
1097
- подстановкой: `time.locale` = `en` → `actual: 'en'` против ожидаемого `'ru'`, `exit 1`. Заодно это первая проверка, которая
1098
- читает **тег**, а не текст: значение, управляющее форматированием, проверяется тем, что оно
1099
- производит.
1100
- - **«По Пн, Ср в 09:00» — фраза, которую не видела ни одна построчная проверка.** Аудит
1101
- сверяет значение с его собственным шаблоном, поэтому фраза, верная в одиночку, но сломанная
1102
- подстановкой, остаётся невидимой: `frequency.weekly` был переведён как
1103
- «По {weekdays} в {time}», а `{weekdays}` приходит как «Пн, Ср» — «По» требует дательного
1104
- множественного («по понедельникам»), которого у сокращений нет. Теперь «{weekdays} в {time}
1105
- ({timeZone})» → «Пн, Ср в 09:00 (Europe/Moscow)». Заодно появился `check:composition`:
1106
- три проверки складывают реальные значения так, как это делает харнесс, и читают результат
1107
- целиком — еженедельное правило, ежедневное с зоной и интервал с формами числа
1108
- («Раз в 2 дня», «Раз в 5 дней», «Раз в 1 минуту»). Входит в `verify` и в CI. Доказано
1109
- подстановкой: возвращённое «По {weekdays}» даёт ровно `actual: 'По Пн, Ср в 09:00 …'`
1110
- против ожидаемого, `exit 1`.
1111
- - **`check:readmes` не зависел от перевода строк в рабочей копии.** Скрипт резал файл по
1112
- `\n` и искал заголовки выражением `^(#{1,6})\s+(.*)$`: в строке, заканчивающейся `\r\n`,
1113
- точка не доходит до `\r`, и заголовок не находился. На Linux (LF) всё проходило, а после
1114
- `git checkout` на Windows с `autocrlf` тот же файл давал «заголовков 0» — гейт падал на
1115
- исправном документе. Переводы строк нормализуются перед разбором: проверка описывает
1116
- документ, а не машину, на которой его прочли. Доказано, что различие при этом всё ещё
1117
- ловится: лишний заголовок в `README.en.md` → «заголовков 16, а в README.md 15», `exit 1`.
1118
- - **Копия английского с другим регистром тоже ловится.** Сверка остатков требовала точного
1119
- совпадения с шаблоном, поэтому `Cancel` при шаблоне `cancel` проходила: на экране это
1120
- непереведённый английский текст, а все проверки зелёные. Теперь значение, отличающееся от
1121
- шаблона только регистром или краевыми пробелами, — такая же копия и требует решения;
1122
- нормализуется ровно это, поэтому настоящее изменение слова по-прежнему идёт через точную
1123
- сверку и политику. Доказано подстановкой: `baseUrl` = `BASE URL` → «совпадает с шаблоном
1124
- без учёта регистра», `exit 1`; после откатa — 0.
1125
- - **Две проверки приведены в согласие со своими обещаниями.** Сверка плейсхолдеров сравнивает
1126
- их **по порядку** (перестановка `{a}`/`{b}` — это подстановка числа не в тот слот), а
1127
- комментарий обещал множество; обещание исправлено, а не поведение. У `check:size` допуск
1128
- был 2 КБ при тексте «1 КБ» в README и в собственном комментарии — теперь ровно 1 КБ,
1129
- проверено подстановкой: заявленные 392 КБ при пакете 394 КБ валят сборку, хотя раньше
1130
- проходили.
1131
- - **Проверено обратное направление «одна фраза — один перевод».** Гейт ловит одну английскую
1132
- фразу, переведённую по-разному; теперь печатается и зеркало — одинаковый русский ответ на
1133
- **разные** английские фразы от четырёх слов. Это след копипасты (подсказка, приехавшая от
1134
- соседнего ключа) или два разных факта, слитых в одну фразу. Гейтом это быть не может:
1135
- русский законно сливает слова, которые английский различает («Save» и «Keep» — оба
1136
- «сохранить»), поэтому печатаются обе стороны, а решение остаётся за человеком. Сейчас
1137
- находок 0; проверено подстановкой: копия длинного значения `keyHelp` в `keyPlaceholderSaved`
1138
- дала ровно одну строку с обеими английскими фразами.
1139
- - **Свои формы числа тоже под гейтом.** Харнесс передаёт счёт ключам, которые никогда не
1140
- паровал (`terminal.expand`, `terminal.expandAria`, `terminal.expandRest` и их двойники в
1141
- менеджере плагинов) — там формы держатся только на пакете, и потеря `.few` не проявилась бы
1142
- ни в чём: обёртка молча вернулась бы к базовому ключу без склонения. Проверка распространена
1143
- на эти базы: у каждой должны быть `.few` и `.many`. Доказано подстановкой: удалённый
1144
- `terminal.expand.few` → `job · terminal.expand.few`, `exit 1`; после откатa — 0.
1145
- - **Прогон в CI раз в неделю.** Перевод гниёт и без наших правок: харнесс уезжает вперёд,
1146
- локально всё зелено, а расхождение всплывает только при сборке у пользователя. В
1147
- `verify.yml` добавлено расписание (понедельник, 06:00 UTC): тот же полный `verify` против
1148
- закреплённого тега, без пуша и без человека.
1149
- - **Метаданные для поиска.** В `keywords` добавлены `dsh-plugin` (по нему ищут в маркетплейсе
1150
- плагинов), `locale` и `typography` — сам пакет и его правила теперь находятся по тем
1151
- запросам, которыми их ищут.
1152
-
1153
- ### Исправлено
1154
-
1155
- - **Аудит перестал пропускать целые словари**: ключи без кавычек (`panel: '插件'`
1156
- в менеджере плагинов) и словари с аннотацией типа
1157
- (`export const zh: Record<Key, string> = {` в голосовом вводе, субагенте и
1158
- траектории) не читались, поэтому проверка плейсхолдеров смотрела 1621 значение
1159
- вместо 2462. Сверка идёт по всем шаблонам ключа, а не по первому найденному:
1160
- разные ключи `invalid` в трёх namespace больше не дают ложного расхождения.
1161
- - **Повторная регистрация `ru` больше не срывает активацию** (PR #3, автор @knopki):
1162
- когда другой языковой пакет (`@linxin666/dsh-i18n` — первый известный случай)
1163
- регистрирует `ru` или занимает namespace раньше, пакет пропускает ровно эту ошибку
1164
- дублирования, продолжает регистрировать остальные словари и не забирает себе чужие
1165
- ресурсы. До этого активация обрывалась на `locale "ru" is already registered`,
1166
- и русские словари ядра не подключались вовсе.
1167
- - **`message.turnProcess.took` больше не показывает плейсхолдер** (PR #2, автор @knopki):
1168
- харнесс рендерит этот ключ без параметров и сам добавляет длительность, поэтому
1169
- `Заняло {duration}` попадало на экран как «Заняло {duration}7мин 26с». Теперь это
1170
- просто префикс `Заняло `. Тем же изменением удалён мёртвый ключ
1171
- `message.turnProcess.deepDivingFor` — живой ключ называется `chat.deepDivingFor`.
1172
- - **Счётчик переводов в README приведён к факту**: 2417 вместо 2418 — удаление мёртвого
1173
- ключа уменьшило набор на одну строку, а `check:readmes` сверяет структуру, а не числа.
1174
-
1175
- ### Изменено
1176
-
1177
- - **Пакет стал в семь раз легче: 2,6 МБ → 354 КБ.** Баннер для README переведён из PNG
1178
- в JPEG (1612 → 152 КБ) без потери качества; иконка и карта исходников в npm больше не
1179
- входят. Иконка остаётся в репозитории — каталог плагинов ссылается на неё абсолютным
1180
- адресом, — а карта исходников остаётся в git для установки из репозитория.
1181
- - **Декларации типов генерируются сборкой.** В `lib/` лежали `index.d.ts` и `client.d.ts`
1182
- от 2 сентября, причём `lib/typography.d.ts`, на который ссылался `exports`, не
1183
- существовал вовсе. Теперь `tsdown` emits `index.d.ts` и `typography.d.ts` из текущего
1184
- исходника, а `./client` больше не обещает файл типов: это модуль для клиентского
1185
- загрузчика с собственным баннером, а не TypeScript-API.
1186
- - **`@deepseek-ai/dsh-client-locale` в devDependencies закреплён точно на версии тега** —
1187
- `0.2.1-alpha.3`, вместе с `@deepseek-ai/cordis@4.0.5-alpha.2`. Плавающий диапазон
1188
- резолвился в чужую комбинацию и ронял `npm install` в CI с `ERESOLVE`, то есть сборка была
1189
- красной не из-за кода. Закреплённая пара даёт то, ради чего она нужна: `check:locale`
1190
- и `check:plural` выполняются на том же рантайме локали, который получают пользователи, а
1191
- не на более старом `0.2.0-rc.2`. Публикуемый peer-диапазон `@deepseek-ai/cordis` расширен
1192
- до `^4.0.2 || >=4.0.5-0 <4.0.6-0`: иначе предрелиз `4.0.5-alpha.2` из тега не проходит
1193
- проверку peer-зависимостей.
1194
-
1195
- ## [0.2.0] — 2026-10-03
1196
-
1197
- ### Добавлено
1198
-
1199
- - **Синхронизация с `dsh 0.2.1-alpha.1`**: 76 новых ключей — пять новых namespace
1200
- (`settings.agentPreset`, `settings.account`, `inspectorPanel`, `claude-code-mods`,
1201
- `session-inspector`) и три расширенных (`pluginManager` +8, `workspace` +2,
1202
- `schedule.manager` +1). Покрытие — **2364 из 2364** ключей новой базы.
1203
- - Переведены панель инспектора сессий (52 строки), установка плагинов из npm/Git/каталога,
1204
- режим создания плагина агентом и напоминание о том, что сессия субагента не получает
1205
- доставленные уведомления.
1206
-
1207
- ### Исправлено
1208
-
1209
- - **Три имени namespace сверены с кодом харнесса**: предположения по документации
1210
- (`agent-preset`, `account`, `inspector`) оказались неверными — настоящие имена
1211
- `settings.agentPreset`, `settings.account` и `inspectorPanel`. Переводы под
1212
- неверными именами не показывались бы никогда.
1213
- - **Аудит получил новый гейт**: namespace, объявленный дважды, перекрывает сам себя в
1214
- объектном литерале и молча роняет все переводы внутри, при этом покрытие по ключам
1215
- остаётся полным. Проверено: с дублем аудит падает с кодом 1.
1216
-
1217
- ### Изменено
1218
-
1219
- - Версия пакета поднята до **0.2.0** — пакет следует за веткой харнесса 0.2.x.
1220
- - Аудит в CI переведён на тег `dsh-v0.2.1-alpha.1`.
1221
-
1222
- ## [0.1.9] — 2026-10-02
1223
-
1224
- ### Добавлено
1225
-
1226
- - **Синхронизация с `dsh 0.2.0-rc.2`**: 37 новых ключей в шести namespace — `chat`
1227
- (единицы длительности и строка «Углублённый анализ»), `conversation` (вопросы,
1228
- ожидающие ответа), `model` (пустой поиск), `pluginManager` (подсказки установки,
1229
- несовместимость версий, порядок обновления), `question` (асинхронные вопросы:
1230
- ожидание, ответы, пропуск) и `workspace` (сессии без названия).
1231
- Покрытие — **2287 из 2287** ключей новой базы.
1232
- - **Расширен диапазон совместимости** до `>=0.1.0-rc.2 <0.3.0`: контракт локали
1233
- (`addLanguage` и `register`) в 0.2.0 не изменился, проверено по дереву тега.
1234
-
1235
- ### Изменено
1236
-
1237
- - Аудит в CI переведён на тег `dsh-v0.2.0-rc.2`; проверка против 0.1.7-rc.2
1238
- по-прежнему проходит (2264 из 2264), поэтому пакет работает на обеих ветках.
1239
-
1240
- ## [0.1.8] — 2026-09-27
1241
-
1242
- ### Добавлено
1243
-
1244
- - **Русская экранная типографика** (`src/client/typography.ts`): «ёлочки» вместо прямых
1245
- кавычек, длинное тире между словами, многоточие, неразрывный пробел между числом и
1246
- следующим словом или единицей измерения. Правила осторожные: код, поля ввода,
1247
- редактируемый текст и `[data-typography="off"]` не трогаются, повторный проход ничего не
1248
- меняет. Работает как `ctx.effect`, поэтому выгружается вместе с плагином.
1249
- - **Проверки в CI** (`.github/workflows/verify.yml`): сборка, 12 правил типографики
1250
- (`scripts/check-typography.mjs`) и аудит локали (`scripts/audit.mjs`) против
1251
- **закреплённого тега** харнеса. Новые npm-скрипты: `check:typography`, `audit`, `verify`.
1252
- - Раздел README **«Область покрытия»** с измерением: из 17 самых скачиваемых плагинов
1253
- экосистемы 11 не поставляют собственных строк, поэтому их русский интерфейс обеспечивают
1254
- словари харнеса; словари сторонних плагинов в область пакета не входят.
1255
-
1256
- ### Исправлено
1257
-
1258
- - **Число переводов в документации**: 2598 → **2304**. Прежняя цифра считала повторяющиеся
1259
- имена ключей в разных namespace дважды.
1260
-
1261
- ## [0.1.7] — 2026-09-27
1262
-
1263
- ### Исправлено
1264
-
1265
- - **Мёртвый namespace `schedule.catalog`.** В 0.1.6 было ошибочно записано, что этот
1266
- namespace упразднён и заменён на `schedule.manager`. На деле в харнесе существуют оба:
1267
- `schedule.catalog` — каталог напоминаний в шапке сессии вместе с частотами и единицами
1268
- времени, `schedule.manager` — менеджер задач автоматизации. Из-за неверного вывода
1269
- около 30 строк перевода (`trigger.*`, `list.*`, `delete.*`, `frequency.*`, `cron.*`,
1270
- `unit.*`, `relative.*`, `mark.aria`, `hover.more`) регистрировались под именем, которое
1271
- никто не читает, и никогда не показывались в интерфейсе.
1272
- - **Числительные, неверные на числах 2–4.** Русский требует трёх форм для `n ≥ 2`, а
1273
- компоненты харнесса выбирают форму как `count === 1 ? 'ключ.one' : 'ключ.other'`.
1274
- Переписаны 17 пар ключей (`.one` — естественная форма для `n = 1`, `.other` —
1275
- конструкция, корректная при любом `n ≥ 2`) и 8 строк с `{count}` перед склоняемым
1276
- словом. Пример: «2 субагентов» → «Субагентов: 2», «каждые 2 часов» → «каждые 2 ч»,
1277
- «ещё 3 строк» → «ещё 3 стр.».
1278
- - Единицы измерения в частоте повтора: `unit.hour.other` «часов» → «ч» и аналогично
1279
- для минут, секунд и дней; `cron.time.everyMinutes` «минут» → «мин».
1280
-
1281
- ### Добавлено
1282
-
1283
- - **Namespace `schedule.manager` (188 ключей)** — менеджер задач автоматизации полностью
1284
- на русском: панель и каталог задач, поиск, фильтр состояний, сведения о задаче и её
1285
- вкладки, журнал запусков с правилами хранения, редактор расписания (дата, время, час,
1286
- минута, секунда, часовой пояс, интервал повтора), правила повтора (разово, каждые
1287
- N минут/часов/секунд, ежедневно, понедельник — пятница, еженедельно, свой cron),
1288
- cron-форма с выбором дат и минуты, удаление с подтверждением, уведомления, карточки.
1289
- 55 ключей приходят из общего frequency-словаря, поэтому их текст совпадает с каталогом.
1290
- - 7 ключей: `blocked.composer`, `defaultWorkspace.title`, `schedule.active`,
1291
- `settings.transcript.expanded`, `status.scheduled`, `status.overdue`, а также namespace
1292
- `shortcuts.layout` с подписью команды показа левой панели.
1293
-
1294
- ### Инструменты
1295
-
1296
- - Аудит **имён** namespace (`audit-namespaces.mjs`): имена извлекаются из всех способов
1297
- объявления (`const NS`/`SETTINGS_NS`/`namespace`, `locale.register`, `locale.bind`,
1298
- `PropsLocale<…>`) и сравниваются множествами. Прежняя проверка покрытия сверяла ключи
1299
- внутри namespace, но не сверяла имена, из-за чего целый namespace мог тихо не работать.
1300
-
1301
- ### Состояние
1302
-
1303
- 52 namespace, 2304 перевода, ~2700 строк. Проверки: 0 отсутствующих ключей против
1304
- `dsh 0.1.7-rc.2`, 0 мёртвых namespace, 45/45 пар ключей с формами числа.
1305
-
1306
- ## [0.1.6] — 2026-09-25
1307
-
1308
- Догнал `dsh 0.1.7-rc.2`: 252 ключа добавлено, 18 обновлено, 26 удалено.
1309
- Появились разделы `shortcuts` (редактор горячих клавиш), `schedule.manager`,
1310
- `settings.account`; исправлена единица счёта в инвентаре плагинов («187 плагины» →
1311
- «187 плагинов»); добавлены npm keywords, homepage, badges и снимки интерфейса; исправлен
1312
- peer-диапазон `@deepseek-ai/dsh-client-locale` на `>=0.1.0-rc.2 <0.2.0` (старый `^0.1.0`
1313
- не разрешался, так как пакет публикуется только под prerelease-версиями).
1314
-
1315
- ## [0.1.5] — 2026-09-24
1316
-
1317
- Догнал `dsh 0.1.7-rc.1`: 49 ключей добавлено, 13 обновлено, 16 удалено, namespace
1318
- `directory-browser` убран. Переведены подписи шагов работы инструментов, полноэкранный
1319
- просмотр изображений, режимы показа ходов, установка плагинов из GitHub, сообщения команды
1320
- `Cordis`. Уточнены формулировки там, где upstream заменил «teammate/agent» на «subagent»,
1321
- а «Beta» — на «Experimental».
1322
-
1323
- ## [0.1.4] — 2026-09-23
1324
-
1325
- Догнал `dsh 0.1.7-alpha.2`: 905 ключей добавлено, 29 изменены, 126 удалены. Появились
1326
- разделы `pluginManager` (встроенная панель плагинов), `settings.account`, семейство
1327
- `settings.agentLoop`/`settings.shell`/`settings.subagent`/`settings.webSearch`,
1328
- `sidebarBrowser`, `sidebarExcel`, `sidebarOffice`, `voice-input`.
1329
-
1330
- ## [0.1.3] — 2026-09-20
1331
-
1332
- Догнал `dsh 0.1.6-alpha.1`: 139 ключей добавлено, 7 новых разделов, 28 устаревших ключей
1333
- удалено.
1334
-
1335
- ## [0.1.2] — 2026-09-19
1336
-
1337
- Добавлены разделы `open-in-app`, `sidebarFiles`, `sidebarRight`, `sidebarCodePreview`.
1338
-
1339
- ## [0.1.1] — 2026-09-18
1340
-
1341
- Первый публичный выпуск: регистрация языка `ru` и словари по namespace.
1
+ # Changelog
2
+
3
+ Все значимые изменения пакета. Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/),
4
+ версии — по [SemVer](https://semver.org/lang/ru/).
5
+
6
+ Пакет следует за харнессом: целевая версия — `dsh 0.2.1-alpha.1` и новее
7
+ (`>=0.1.0-rc.2 <0.3.0` для `@deepseek-ai/dsh-client-locale`).
8
+
9
+ ## [0.2.14] — 2026-10-21
10
+
11
+ ### Добавлено
12
+
13
+ - **Ещё 39 словарей из поиска по экосистеме: +6 488 строк.** Пакет вырос до 520 словарей.
14
+ Поиск по реестру npm нашёл 40 плагинов со своим словарём, которых у нас не было; разбор
15
+ их сборок подтвердил 6 491 новый ключ. Крупнейшие: `wallpaper-engine` (987),
16
+ `dshAdmin` (898 вместо 2 — прежняя выгрузка читала не тот объект), `clawock` (459),
17
+ `worktree` (410), `llm-verifier` (344), `clearai` (297), `settings.dshEars` (235),
18
+ `git-worktree` (209), `EMPTY_RESPONSE` (194), `settings-search-v5` (145),
19
+ `task-supervisor` (143), `doctor` (138), `git` (135), `usageStats` (134),
20
+ `cpa-panel` (126), `bigfish` (113), `dsh-electro-lab` (112), `settings.skills` (112),
21
+ `dsh-winrm` (110), `undo` (108), `dsh-console` (105). Три имени оказались заняты
22
+ словарями других плагинов (`dsh-automation`, `remote`, `settings.ip-pool`) — их
23
+ оставили прежним владельцам, чтобы не подменить перевод чужого продукта.
24
+ - **Проверка «покрыт ли плагин» стала измеряемой.** `scan-ecosystem.mjs` ищет пакеты со
25
+ своим `locale.register`, а `scan-gap.mjs` прогоняет по ним наш разборщик и сверяет
26
+ ключи с пакетом: «цикл» в поиске ещё не значит «покрыт».
27
+ - **Аудит: непарная кавычка шаблона прощает нашу.** Склеиваемые словари пишут кавычку как
28
+ голый `"` — `If "` открывает подпись, которую дописывает код. Раньше такое прощение
29
+ срабатывало только для «ёлочек» и «лапок» в самом шаблоне, из-за чего правильная русская
30
+ «ёлочка» в склейке считалась дефектом.
31
+
32
+ - **Словари 158 плагинов обновлены до свежих публикаций: +2 716 строк.** Свежая выгрузка
33
+ принесла 3 196 новых ключей и 207 изменённых текстов; 2 289 значений взяты из уже
34
+ сделанных переводов (тот же английский текст), 1 026 переведены заново. Заметнее всего
35
+ выросли `dsh-session-kit` (49 → 721), `engram` (402 → 408), `agency` (92 → 240),
36
+ `dsh-clinebot` (206 → 368), `settings.shop` (10 → 131), `settings.codeIndex` (12 → 108),
37
+ `dsh-subscriptions` (293 → 360), `dsh-kanban` (420 → 501),
38
+ `settings.dshopencodego` (93 → 174), `dsh-key-limits` (140 → 198).
39
+ - **Ошибка разбора бандла, из-за которой словари читались не из того объекта.**
40
+ Сокращённая запись `locale.register(NS, { en, zh })` разбиралась окном в 200 символов:
41
+ проба сокращённого свойства не срабатывала из-за ведущего пробела, и разбор уходил в
42
+ соседний вызов. В итоге 26 словарей попали в пакет с чужим объектом — у
43
+ `dsh-github-ops` вместо 146 ключей оказался 1, у `dsh-key-rotation` вместо 251 — 2,
44
+ у `settings.commandcode` вместо 270 — 138 (словарь панели вместо словаря настроек).
45
+ Теперь аргументы вызова разбираются целиком, а namespace и словарь берутся из одного
46
+ и того же вызова.
47
+ - **Ключи, которые плагины всё ещё показывают, возвращены (133).** Обновление пакета
48
+ убирает ключи, которых больше нет в публикации, но прежняя выгрузка местами теряла
49
+ живые: перед удалением ключ сверяется с бандлом свежей версии, и оставшийся в нём
50
+ остаётся в словаре с прежним переводом.
51
+
52
+ - **Словари сторонних плагинов: ещё 414, всего 520.** За этот релиз их стало 520, а пакет
53
+ покрывает 584 namespace и 67 724 строки (из них 3 040 — namespace самого харнесса,
54
+ 64 684 — словари плагинов). Крупнейшие новые: `dsh-graph.hidden-versions.`
55
+ (1 206 ключей), `dsh-task-board` (963), `settings.codexSubscription` (841),
56
+ `research` (831), `mnemon` (753), `tracescope` (611), `settings.promptSetting` (586),
57
+ `ui-mpkg-wallpaper` (546), `dsh-workspace-combiner` (527), `dsh-top100` (510),
58
+ `settings.xdpool` (500), `dsh-agent-extension-preset` (464), `settings.knowledge` (449),
59
+ `settings.workbuddy-xdpool` (427), `dsh-plugin` (406), `engram` (402), `sessions` (399),
60
+ `dsh-live-voice` (395), `dshhub-market` (393), `dsw` (377), `usageBilling` (351),
61
+ `settings.archived-chats` (344), `theone` (341), `settings.openai-codex` (340),
62
+ `settings.grok-build` (337), `dsh-lowtide` (332), `dshCcStudio` (327), `git-review` (322),
63
+ `settings.trae` (318), `conversation.session.header.actions` (303), `xiaomi-mimo-tts` (303),
64
+ `dshm` (302), `providerUsage` (293), `sessionGraph` (288), `session-manager` (282),
65
+ `dsh-remote` (278), `dsh-plugin-cards` (276), `archive-manager-workspace` (272),
66
+ `dshTimeline` (272), `ds-harness-remote` (270), `instruction-memory` (269),
67
+ `comfyui` (268), `dsh-inbox` (264), `notifier` (260), `dsh-automation` (255),
68
+ `local.vcs` (251), `agentStudio` (247), `swarm` (246), `trust` (246), `chaos` (239),
69
+ `token-monitor` (231), `dsh-database` (218), `roundtable` (215),
70
+ `settings.deeppilot` (215), `modeltester` (212), `agentTeams` (210),
71
+ `dsh-auto-title` (209), `settings.pluginManagerPro` (209), `webSearchEnhanced` (207),
72
+ `voice-mode` (205), `agentlex-moot-court` (202), `settings.kinich-theme` (200),
73
+ `settings.packyCodeApi` (200), `solution-explorer` (194), `usage` (194),
74
+ `whale-pet` (186), `dsh-perm-gate` (184), `themeManager` (178), `settings.opencode-go` (177), `settings.tokenUsage` (176), `reverse-proxy` (175), `dsh-skillbox` (173), `retrace` (172), `settings.dsh-autostart-workbuddy` (171), `settings.model-capability` (170), `settings.pluginCapabilities` (169), `llm-qoder` (168), `apiDashboard` (166), `settings.subusage` (164), `version-update` (164), `betterWorkspace` (163), `settings.codebuddy-cli` (162), `settings.mcpManager` (160), `token-ledger` (160), `dsh-llm-hub` (159), `dsh-mnemosyne` (159), `xrn1997-dsh-novel` (159), `dsh-fonttune` (158), `dsh-vsceditor` (158), `dsh-bill` (154), `dsh-decision-layer` (152), `dsh-pentester-view` (152), `misakanet.panel.density.v1` (148), `dsh-update-copilot` (146), `dsh-pdf-mineru` (141), `workload-report` (141). Английский текст каждого словаря выгружен из его собственной
75
+ публикации (`npm run fetch:plugins`) вместе с версией и лицензией, перевод сделан
76
+ с этого английского текста, а не с чужого пакета локали.
77
+ - **Ещё 30 словарей плагинов (партия 44).** `dsh-skills-manager-plus` (102), `dsh-message-ops` (101), `quota` (99), `settings.runninghub` (98), `model-pricing` (97), `dsh-scheduler` (96), `dsh-web-search` (96), `pocketMobileNav` (96), `net-proxy` (95), `settings.acp` (95), `git-panel` (94), `russian-lang` (94), `dsh-prompt-manager` (93), `settings.dshopencodego` (93), `cost-meter` (92), `dsh-ballast` (92), `dsh-instance-manager` (92), `harden` (92), `meow-memory` (92), `token-meter-panel` (92), `free-proxy` (88), `hermes-loop` (88), `balance` (87), `dsh-delete-session` (87), `notify-sounds` (87), `dsh-jev-advisor` (85), `dsh-session-steward` (85), `dsh-pomodoro` (83), `dsh-project-memory` (83), `memory-porter` (83). Английский текст каждого
78
+ словаря выгружен из его собственной публикации (`npm run fetch:plugins`) вместе с версией и
79
+ лицензией, перевод сделан с этого английского текста, а не с чужого пакета локали.
80
+ - **Ещё 30 словарей плагинов (партия 45).** `im-gateway` (82), `lume` (82), `pluginTopology` (82), `settings.grokSubscription` (82), `catppuccin` (81), `clutch.title` (80), `fileExplorer` (79), `noletme` (79), `settings.codexAuth` (79), `settings.kimiSubscription` (78), `settings.multi2api` (78), `genui` (77), `gitbashShell` (77), `settings.modelProxy` (77), `settings.openai-codex-auth` (77), `dsh-ding` (75), `settings.modelContextCatalog` (75), `tokenLedger` (75), `usage-state` (75), `autoMemory` (73), `skill-center` (73), `capability-panel` (72), `dsh-desktop-restart` (72), `dsh-service-console` (72), `usage-meter` (72), `bgjobs` (71), `ds-balance` (71), `file-review` (71), `oomol.settings` (71), `settings.dreamSkin` (71). Английский текст каждого
81
+ словаря выгружен из его собственной публикации (`npm run fetch:plugins`) вместе с версией и
82
+ лицензией, перевод сделан с этого английского текста, а не с чужого пакета локали.
83
+ - **Ещё 30 словарей плагинов (партия 46).** `.runs.json` (70), `dsh-skins.ui` (69), `settings.lanProxy` (69), `settings.qoderCredential` (69), `dsh-provider-veark` (68), `quota-panel` (68), `dsh-tree-view` (67), `settings.researchMarket` (67), `skillManager` (67), `dsh-canvas` (66), `personaDafeiyu` (66), `prompt-library` (66), `completion-alert` (65), `dsh-session-toolbox` (65), `dsh-speak` (65), `dsh-treekeeper` (65), `dsh-emoji` (64), `dsh-mcp-manager-plus` (64), `harness-channel-config` (64), `dsh-ppt` (63), `nested-followups` (63), `office-ppt` (63), `relay.codex` (63), `harbor-evolution` (62), `baize-rules` (61), `dsh-prompt-history` (61), `task-runner` (61), `settings.cursorSubscription` (59), `whale-meter` (59), `codex-navigation-enhancer` (58). Английский текст каждого
84
+ словаря выгружен из его собственной публикации (`npm run fetch:plugins`) вместе с версией и
85
+ лицензией, перевод сделан с этого английского текста, а не с чужого пакета локали.
86
+ - **Ещё 30 словарей плагинов (партия 47).** `dsh-sm-version-display` (57), `smartdl` (57), `ui-quote` (57), `artifact` (56), `dsh-env-inspector` (56), `dsh-notify-yimit` (55), `labnana` (55), `mcp-skill-manager` (55), `pptwise` (55), `agent-skills` (53), `opencodeFree` (53), `searchPool` (53), `dsh-shortcuts` (52), `feishu-assistant` (52), `ticktick` (52), `image-plugin` (51), `theme-studio` (51), `magpie-connect` (50), `mcp-ui` (50), `dsh-mcp-market` (49), `dsh-session-kit` (49), `repeat-tool-breaker.card` (49), `settings.checkpointRewind` (49), `contextMemory` (48), `peakrate-model` (48), `workspace-prompt` (48), `fallbacks` (47), `dsh-web-icon-indicator` (45), `settings.chatTranslate` (45), `settings.codebuddy` (45). Английский текст каждого
87
+ словаря выгружен из его собственной публикации (`npm run fetch:plugins`) вместе с версией и
88
+ лицензией, перевод сделан с этого английского текста, а не с чужого пакета локали.
89
+ - **Ещё 30 словарей плагинов (партия 48).** `settings.nico` (44), `taskpilot` (44), `approval-review` (43), `liao-sidebar-organizer` (43), `prompt-enhance` (43), `settings.plugins.zai` (43), `provider-usage` (42), `settings.serperSearch` (42), `dsh-discord` (41), `git-credentials` (41), `viewManager` (41), `dsh-antigravity` (40), `rq-model-router` (40), `context-zip` (39), `drawio` (39), `dsh-github-router` (39), `dsh-round-rightclick` (39), `dsh-token-usage-sidebar` (39), `power.button` (39), `prompt_optimizer` (39), `session-title-pattern` (39), `settings.permissionRules` (39), `dsh-villager-hmm` (38), `multi-folder` (38), `tidy-display` (38), `user-management` (38), `web-search-aggregation` (38), `dsh-delete-turn` (37), `settings.autotier` (37), `ai4scholar` (36). Английский текст каждого
90
+ словаря выгружен из его собственной публикации (`npm run fetch:plugins`) вместе с версией и
91
+ лицензией, перевод сделан с этого английского текста, а не с чужого пакета локали.
92
+ - **Ещё 30 словарей плагинов (партия 49).** `orbit-model` (36), `plugin.intellijMcp` (36), `settings.dshProxy` (36), `settings.subagentModel` (36), `dsh-context-imports` (35), `lcx-codex` (35), `settings.budget` (35), `vision` (35), `costTrack` (34), `dsh-desktop` (34), `win-pilot` (34), `autoReviewPanel` (33), `dsh-jev-tools` (33), `dsh-llm-trace` (33), `dsh-movein` (33), `openrouter-providers` (33), `vision-recognizer` (33), `dsh-doc-impact` (32), `dsh-plugin-message-tree` (32), `settings.llm-proxy` (32), `custom-headers` (31), `dsh-theme` (31), `stepTrace` (31), `bookmarks` (30), `dsh-mineru` (30), `image-pathify` (30), `select-quote` (30), `ui-background` (30), `dsh-argp` (29), `dsh-improve-prompt` (29). Английский текст каждого
93
+ словаря выгружен из его собственной публикации (`npm run fetch:plugins`) вместе с версией и
94
+ лицензией, перевод сделан с этого английского текста, а не с чужого пакета локали.
95
+ - **Ещё 30 словарей плагинов (партия 50).** `verdict` (29), `web-fetch-playwright` (29), `daruma` (28), `dsh-chatgpt-subscription` (28), `dsh-image-viewer` (28), `windowsComputerUse.settings` (28), `workbuddy-console` (28), `modelSelector` (27), `promptVcs` (27), `dsh.eval-control` (25), `modelArena` (25), `toolStats` (25), `draw` (24), `conversation-anchors` (23), `diff-stat` (23), `dsh-rerun-turn` (23), `markdown-input` (23), `settings.advisor` (23), `settings.conversationUi` (23), `settings.dsh-zhipu` (23), `web-search-anysearch` (23), `dshmarketplace` (22), `formatforge` (22), `ui-rainbowspeak` (22), `agentLang` (21), `settings.pluginInstall` (21), `jobProgress` (20), `settings.composerLayout` (20), `dsh-plugin-smooth-stream` (19), `settings.anysearchApiKey` (19). Английский текст каждого
96
+ словаря выгружен из его собственной публикации (`npm run fetch:plugins`) вместе с версией и
97
+ лицензией, перевод сделан с этого английского текста, а не с чужого пакета локали.
98
+ - **Ещё 30 словарей плагинов (партия 51).** `zhihu-search` (19), `approvalVoice` (18), `better-display` (18), `dsh-file-attachment` (18), `hardwareMonitor` (18), `theme-gallery` (18), `sessionColor` (17), `settings.reach` (17), `settings.ths-holdings` (17), `dsh-llm-provider` (16), `settings.modelVisibility` (16), `settings.talk` (16), `inline-comments` (15), `tradeSearch` (15), `dsh-model-search` (14), `markdown-preview` (14), `reasoning-effort` (14), `cursor-effect` (13), `dsh-conversation-outline` (13), `ERR_UNSAFE_PROFILING_DIRECTORY` (13), `dsh-github` (12), `global-rules` (12), `inlineMedia` (12), `peakIndicator` (12), `ptcCordis` (12), `publish` (12), `session-colorful-unread-pin-jobs` (12), `settings.catppuccin` (12), `settings.codeIndex` (12), `w2m.target` (12). Английский текст каждого
99
+ словаря выгружен из его собственной публикации (`npm run fetch:plugins`) вместе с версией и
100
+ лицензией, перевод сделан с этого английского текста, а не с чужого пакета локали.
101
+ - **Ещё 30 словарей плагинов (партия 52).** `quote-followup` (11), `settings.shop` (10), `traceCompare` (10), `dsh-task-worktree` (9), `settings.browser-harness` (9), `chart` (7), `chat-timeline` (7), `dsh-film-studio` (7), `dsh-model-capability` (6), `dsh-vscode-mode` (6), `music` (6), `codex` (5), `dsh-harmony-bootstrap` (5), `liang.skin` (5), `settings.blue-whale` (5), `settings.neu` (5), `devDashboard` (4), `dsh-whale-particles` (4), `mimo` (4), `settings.dracula` (4), `skills-anywhere` (4), `status-rotator` (4), `xingyuan` (4), `dsh-concise` (3), `dsh-openrouter-live` (3), `picturereader` (3), `style` (3), `.ndjson.zst` (2), `agent-shell-consent` (2), `dsh-engram` (2). Английский текст каждого
102
+ словаря выгружен из его собственной публикации (`npm run fetch:plugins`) вместе с версией и
103
+ лицензией, перевод сделан с этого английского текста, а не с чужого пакета локали.
104
+ - **Ещё 17 словарей плагинов (партия 53).** `dsh-novel-solo` (2), `meow-cachebilling` (2), `settings.backupPanel` (2), `smoothStream.chat` (2), `sse` (2), `tool-plus-cards` (2), `agent-community` (1), `agent-schedule` (1), `computer-use` (1), `delegateRouter` (1), `dsh-chat-tidy` (1), `dsh-git-badge` (1), `dsh-thoughtdag` (1), `dshSidebarPlus` (1), `relay.session-import` (1), `remix-router-transitions` (1), `schedule-attention-enhancer` (1). Английский текст каждого
105
+ словаря выгружен из его собственной публикации (`npm run fetch:plugins`) вместе с версией и
106
+ лицензией, перевод сделан с этого английского текста, а не с чужого пакета локали.
107
+ - **Переводы строк, потерянные при выгрузке, восстановлены.** Разбор бандла плагина
108
+ терял обратную косую черту у перевода строки: `\n` читался как буква «n». В английском
109
+ тексте, который мы выгружаем из публикации плагина, это 16 значений — и в двух из них
110
+ («Удалить рабочую область …», «Пентест ещё не запущен») повреждение дошло до русского
111
+ перевода, который читатель видел на экране. Теперь английский сверяется с той
112
+ публикацией, из которой он выгружен, а перевод приводится в соответствие.
113
+ - **Ключи, которые выгрузка теряла, возвращены.** Чтение бандла нашло ключи, которых
114
+ не было в снимке: у `dsh-actions` — подпись боковой панели и кнопка запуска, у
115
+ `dsh-skills-manager-plus`, `pocketMobileNav` и `viewManager` — ещё по одному-двум.
116
+ Теперь эти надписи есть и в английском тексте, и в переводе.
117
+ - **Ещё 30 словарей плагинов.** `settings.security` (138), `auto-continue` (137), `comfyui-tools.panel` (135), `mstar-panel` (134), `dsh-balance` (131), `dsh-proactive` (129), `settings.mcpPanel` (129), `expertSources` (128), `dsh-tavily-pool` (126), `settings.workbuddy2api` (126), `dsh-model-router` (125), `dsh-plugin-rollout-scout` (124), `dsh-updater-npm` (124), `dsh-task-ledger` (122), `dsh-actions` (121), `dsh-zh-settings` (121), `settings.awiki` (121), `ui-enhancer` (120), `atlas` (119), `settings.meme` (113), `dshRemoteExplorer` (111), `h3.internal.event.` (110), `settings.pluginHub` (110), `hypatia-auto-memory` (109), `dsh-dupguard` (108), `session-complete-notify` (107), `dsh-paper-reader` (106), `dsh-notify-me` (105), `dsh-vibedev` (103), `dsh.localLink` (102). Английский текст каждого словаря выгружен из его
118
+ собственной публикации (`npm run fetch:plugins`) вместе с версией и лицензией, перевод сделан
119
+ с этого английского текста, а не с чужого пакета локали.
120
+ - **Переведены надписи, оставшиеся на английском.** 88 значений в словарях плагинов
121
+ были помечены как «сознательно оставленные», но оказались обычными надписями:
122
+ «Select image» → «Выбрать изображение», «Annual quota» → «Годовая квота»,
123
+ «Zoom in» → «Приблизить», «Save video» → «Сохранить видео». Теперь английским
124
+ остаётся только то, что английским и должно быть: названия продуктов и моделей,
125
+ форматы, команды, адреса и идентификаторы — и на каждую такую строку есть
126
+ отдельная запись в `scripts/keep-english.json` с причиной, которую аудит проверяет.
127
+
128
+ ### Исправлено
129
+
130
+ - **Аудит читает значения так, как их видит читатель.** Литерал в исходнике — ещё не
131
+ текст на экране: `\n` это перевод строки, `u2026` — многоточие. Сравнение сырого
132
+ исходника склеивало слова вокруг перевода строки (`npm install lodash\nnpm i lodash`
133
+ читалось как `nnpm`) и прятало типографику каждого значения, где есть перевод
134
+ строки. Теперь escape-последовательности раскодиваются при чтении.
135
+ - **Английский текст, потерявший экранирование, восстановлен.** 78 выгруженных
136
+ словарей хранили английский с повреждёнными последовательностями: там, где
137
+ плагин показывает тире, стояло «u2014», где «·» — «xB7». Разбор бандла когда-то
138
+ терял обратную косую черту, и снимок записал это как есть. Теперь последовательность
139
+ раскодится в символ, а байт — только в одиночку, иначе «x54» в адресе
140
+ github.com/x5427876 стал бы «T».
141
+ - **Скобка, которую закрывает сам плагин, не дефект.** Плагин `dsh-plugin-cards`
142
+ закрывает скобку сам: значение `mk.scopeClose` — это одна закрывающая скобка, которой
143
+ он дописывает то, что открыл в соседней строке. Полноширинная скобка теперь
144
+ считается скобкой наравне с обычной, а значение, чей шаблон сам по себе непарный,
145
+ освобождено от проверки — так же, как значение с непарной кавычкой.
146
+ - **Латинские слова внутри примеров и имён больше не дефект.** Токен в угловых
147
+ скобках (`<working directory>`), в пути (`D:\cloud\dsh-memory`) или в имени
148
+ параметра (`searchProvider`, `fetchProvider`) — это пример, который читатель вводит,
149
+ а не непереведённая проза. В списке разрешённой латиницы появились типы
150
+ кандидата ICE (prflx, srflx, relay), буквальные значения настройки усилия
151
+ (low, medium, high, xhigh) и имена, которые принадлежат плагинам.
152
+ - **Исправлены искажённые переводы в `settings.codexSubscription`.** Четыре строки
153
+ были переведены по искажённому тексту: «Кеш прогнозов евот» вместо «Кэш прогноза
154
+ квоты», «ееш прогнозов» вместо «кэш прогнозов», «лоеальные выбореи» вместо
155
+ «локальные выборки», «провереи восстановят ееш» вместо «проверки восстановят кэш».
156
+ - **Аудит больше не требует форм числа у обычного ключа.** Плагин `theone` везёт
157
+ ключ `settings.other` («Другое») — это пункт списка рядом с `settings.none`, а не
158
+ пара форм числа: харнесс для счёта просит `.one`, а его у плагина нет. Проверка
159
+ требовала от `theone` ещё и `settings.few` / `settings.many` и ругалась на
160
+ вполне обычную строку. Теперь пару формы считает только `.one` (тот же признак,
161
+ по которому пару ищет сам харнесс) или уже переведённая форма `.few` / `.many`.
162
+ - **Подписи в боковой панели настроек уложились в колонку.** «Управление архивом»,
163
+ «Учётные записи и модели» и «Настройки промпта» были длиннее 16 символов —
164
+ столько в колонке не помещается. Теперь «Архив чатов», «Учётные записи» и
165
+ «Промпты».
166
+ - **Пара «счёта / счета» объявлена в `scripts/yo-pairs.json`.** Это одно слово в
167
+ разных формах: «Экспорт счёта (MD)» и «Счета», — и вторая форма пишется без «ё»,
168
+ как положено. Проверка «одно слово — одно написание» считала их разными словами.
169
+ - **Убраны кавычки-ёлочки в непарном виде и лишние пробелы** в переводах плагинов:
170
+ «..» в подсказке о пути, ведущий двойной пробел в метке хоста и «—» без пробела
171
+ в подписи расхода.
172
+
173
+ ## [0.2.13] — 2026-10-20
174
+
175
+ ### Добавлено
176
+
177
+ - **Словари сторонних плагинов: 66.** За этот релиз их стало на семь больше:
178
+ `dsh-context` (595 ключей), `clearai` (297), `dsh-skill-hub` (262),
179
+ `@xfqz86/dsh-usage-stats` (210), `@goodandready/dsh-key-limits` (140),
180
+ `@michengai/dsh-skills-manager` (118), `@michengai/dsh-agency-agents` (92) и
181
+ `dsh-plugins-store` (77). Всего 130 namespace и 14 086 строк, из них 11 053 — в
182
+ словарях плагинов, 3 033 — в namespace самого харнесса.
183
+ - **Выгрузка словарей читает и минифицированные сборки.** Плагин, собранный минификатором,
184
+ пишет `locale.register(Au,{zh:m,en:h})`: namespace — буква в обратных кавычках, словарь —
185
+ объект под именем в одну букву. Раньше такой пакет читался как «слов нет» и терялся.
186
+ Теперь namespace и словарь ищутся по всему пакету, словарь собирается из частей
187
+ (`{ ...enCommon, ...enSkills }`) и находится даже там, где плагин регистрирует его
188
+ через свою обёртку (`addLocale("en", KL_en)`).
189
+ - **Плагин `clearai` оставлен непереведённым.** Его словарь разбит на китайские фрагменты
190
+ предложений, а плагин склеивает их в китайские фразы: фрагмент нельзя перевести
191
+ отдельно, а половина переведённого словаря дала бы в интерфейсе смесь языков.
192
+
193
+ ### Исправлено
194
+
195
+ - **Аудит больше не считает дефектом непарную кавычку, которую открывает сам плагин.**
196
+ Значение `deleteConfirm` у `dsh-key-limits` кончается открытой ёлочкой — закрывающую
197
+ добавляет код плагина. Если и в английском шаблоне кавычка непарная, наша тоже.
198
+ - **Проверка `check:built` больше не врачит о больших бандлах.** `git show` файла больше
199
+ мегабайта упирался в мегабайтный буфер `spawnSync` и завершался ошибкой, а проверка
200
+ читала это как «файла нет в коммите».
201
+
202
+ ## [0.2.12] — 2026-10-20
203
+
204
+ ### Добавлено
205
+
206
+ - **Названия и описания сторонних плагинов — на русском.** Проход, который подставляет
207
+ русское название плагина в отрисованный интерфейс, знал только 32 названия плагинов
208
+ самого харнесса. Теперь в `src/client/plugin-names-ru.json` лежат названия и описания
209
+ ещё 32 сторонних плагинов — тех, чьи словари пакет переводит: английский текст взят
210
+ из их собственной публикации, русский — из того же словаря. Файл собирается
211
+ (`npm run build:plugin-names`), а гейт `npm run check:plugin-names:map` падает, когда
212
+ карта разошлась со словарями. Одно и то же английское название не может быть
213
+ переведено дважды: по-разному у харнесса и у плагина — сборка падает.
214
+
215
+ ### Исправлено
216
+
217
+ - **Проверка собранного бандла перестала врать о больших файлах.** `git show` бандла
218
+ больше мегабайта упирался в мегабайтный буфер `spawnSync` и завершался ошибкой, а
219
+ проверка читала это как «файла нет в коммите»: бандл любого размера выше лимита нельзя
220
+ было сверить с коммитом.
221
+
222
+ ## [0.2.11] — 2026-10-20
223
+
224
+ ### Добавлено
225
+
226
+ - **Словари сторонних плагинов: 59.** За этот релиз их стало на 27 больше:
227
+ `@noob-stupid/dsh-plugin-console` (627 ключей), `@goodandready/dsh-subscriptions` (293 ключей), `@goodandready/dsh-key-rotation` (251 ключей), `@mars-sea/dsh-commandcode-provider` (224 ключей), `@goodandready/dsh-clinebot` (206 ключей), `@goodandready/dsh-remote-workspace` (201 ключей),
228
+ `@goodandready/dsh-server-monitor` (168 ключей), `@goodandready/dsh-model-sync` (167 ключей), `@goodandready/dsh-live-canvas` (161 ключей), `@goodandready/dsh-messenger-gateway` (138 ключей), `@goodandready/dsh-gitea` (132 ключей), `@goodandready/dsh-vision-bridge` (116 ключей),
229
+ `@goodandready/dsh-session-control` (113 ключей), `@goodandready/dsh-github-ops` (109 ключей), `@opencode2dsh/dsh-plugin` (102 ключей), `@goodandready/dsh-issue-reporter` (99 ключей), `@goodandready/dsh-goal` (97 ключей), `@goodandready/dsh-web-gateway` (95 ключей),
230
+ `@goodandready/dsh-shadow-auditor` (74 ключей), `@goodandready/dsh-grok-xsearch` (55 ключей), `@goodandready/dsh-time-machine` (41 ключей), `@goodandready/dsh-approval-gate` (30 ключей), `@goodandready/dsh-smart-restart` (29 ключей), `@wenaixi/dsh-superpower` (19 ключей),
231
+ `@goodandready/dsh-lanmode` (18 ключей), `@goodandready/dsh-context-lens` (11 ключей), `@vlln/dsh-navbar` (2 ключей).
232
+ Всего 123 namespace и 12 590 строк, из них 9 557 — в словарях плагинов,
233
+ 3 033 — в namespace самого харнесса.
234
+
235
+ ### Исправлено
236
+
237
+ - **Аудит больше не теряет ключи словарей плагинов.** Слова `name`, `key`, `type` и
238
+ `interface` отфильтровывались как конструкции TypeScript, но в словаре плагина это
239
+ обычные ключи локализации: четыре значения в `dsh-server-monitor` и `dsh-vision-bridge`
240
+ не проверялись ни одним правилом аудита и считались отсутствующими в своём namespace.
241
+ Теперь фильтр применяется только к namespace самого харнесса.
242
+
243
+ ## [0.2.10] — 2026-10-19
244
+
245
+ ### Добавлено
246
+
247
+ - **Словари сторонних плагинов: 32.** За этот релиз их стало на семь больше:
248
+ `dsh-better-sidebar` (627 ключей), `billion-context` (481), `@goodandready/dsh-kanban` (420),
249
+ `@goodandready/dsh-cron` (396), `@goodandready/dsh-image-gen` (330), `@goodandready/dsh-voice` (321)
250
+ и `dsh-univer-office` — четыре новых ключа. Всего 96 namespace и 9012 строки, из них 5979 —
251
+ в словарях плагинов.
252
+
253
+ ### Исправлено
254
+
255
+ - **Проверка лишних пар форм больше не путает обычный ключ с формой.** Ключ `card.other`
256
+ («Other clients») у плагина `billion-context` оканчивается на `.other` — так же, как форма
257
+ множественного числа, — но основания `card` рядом нет, и проверка называла это дрейфом.
258
+ Теперь вопрос задаётся шаблону: форма, у которой нет основы в самом плагине, — обычный
259
+ ключ этого плагина.
260
+ - **Устаревшее разрешение латинского слова убрано:** «install» разрешён теперь везде, и
261
+ прежнее точечное разрешение больше ничего не разрешало.
262
+
263
+ ## [0.2.9] — 2026-10-19
264
+
265
+ ### Добавлено
266
+
267
+ - **Словари сторонних плагинов: 25.** Пакет переводит не только namespace харнесса, но и словари
268
+ сторонних плагинов: их английский текст выгружается из публикации плагина вместе с версией и
269
+ лицензией (`npm run fetch:plugins`), а перевод сверяется именно с этим снимком, а не с чужим
270
+ русским. В этом релизе добавлены: `dshmarket` (733 ключа), `@goodandready/dsh-tts` (226),
271
+ `@goodandready/dsh-moa` (159), `dsh-better-input` (123), `@goodandready/dsh-cost-meter` (85),
272
+ `dsh-ego-browser` (75), `@goodandready/dsh-usage-guard` (42), `dsh-config-manager` (36),
273
+ `dsh-personal-directive` (5).
274
+ - **Инструмент выгрузки словарей читает чужие форматы.** Ключ, записанный как идентификатор
275
+ (`cancel: "Cancel"`), не попадал в снимок — часть словарей читалась пустыми; регистрация языка
276
+ по одному (`locale.register(NS, "en", en)`) и константа namespace из другого файла пакета тоже
277
+ не распознавались; клиентский бандл не обязательно лежит в `lib/` — `dshmarket` держит его в
278
+ `client/`, и скан только `lib/` находил там словарь хостового модуля. Теперь снимок совпадает с
279
+ тем, что плагин показывает.
280
+
281
+ ### Исправлено
282
+
283
+ - **Отчёт о названиях плагинов больше не считает переведённые непереведёнными.**
284
+ `npm run report:descriptors` перечислял все читаемые названия как непереведённые, даже те, у
285
+ которых в дереве харнесса уже лежал `locale/ru.json`. Теперь отчёт делит их на переведённые и
286
+ оставшиеся и говорит число каждых.
287
+ - **`dsh-plugins-store` убран из покрытия:** то, что выгрузка приняла за словарь, оказалось
288
+ фрагментом JSON Schema — переводить схему значит сломать её.
289
+
290
+ ## [0.2.8] — 2026-10-19
291
+
292
+ ### Добавлено
293
+
294
+ - **Названия плагинов переводятся без правки харнесса.** Название плагина приходит из его
295
+ собственного файла `locale/<язык>.json`, а служба локализации разрешает эту карту, не
296
+ обращаясь к зарегистрированным словарям, — поэтому словарь мог перевести строки плагина,
297
+ но не его имя. Пакет несёт 32 названия сам: английский текст, из которого сделан перевод,
298
+ и русский. Отрисованный интерфейс проходит через проход, который подставляет русский на
299
+ место английского: карточки плагинов, строки состава bundle, страница плагина, реестр в
300
+ настройках, имя для скринридера. Проход берёт только точное совпадение, поэтому фраза, в
301
+ которой название лишь упомянуто, остаётся как была; код, поля ввода и переписка не
302
+ трогаются. Как и типографский, проход работает только пока активен русский. Проверено на
303
+ настоящем документе: `npm run check:plugin-names` (jsdom, 11 случаев).
304
+ - **Патч для харнесса собирается, а не пишется руками.** `docs/harness-descriptors-ru.patch` —
305
+ 32 файла `locale/ru.json` для переведённых плагинов — теперь строится из переводов и дерева
306
+ харнесса (`npm run build:descriptor-patch`), а гейт `npm run check:descriptor-patch` падает,
307
+ когда собранный патч и лежащий в репозитории разошлись. Гейт доказан в обе стороны: с
308
+ изменённым переводом и с вырезанной строкой в патче.
309
+
310
+ ### Исправлено
311
+
312
+ - **Отчёт о названиях плагинов больше не считает переведённые.** `npm run report:descriptors`
313
+ перечислял все читаемые названия как непереведённые, даже те, у которых в дереве харнесса
314
+ уже лежал `locale/ru.json`. Теперь отчёт делит их на переведённые и оставшиеся и говорит
315
+ число каждых.
316
+
317
+ ## [0.2.7] — 2026-10-19
318
+
319
+ ### Исправлено
320
+
321
+ - **Проход типографики больше не правит переписку.** Он переписывал текстовые узлы во всём
322
+ документе, кроме кода, полей ввода и редактируемого содержимого, — а переписка в это
323
+ исключение не попадала: кавычки и тире превращались в «ёлочки» и em dash внутри сообщений,
324
+ и в словах пользователя, и в ответе модели. Пакет правит интерфейс, а чужой текст
325
+ принадлежит автору. Харнесс сам помечает область переписки (`data-conversation-region`), и
326
+ эта метка теперь в списке исключений. Проверено тестом: подпись рядом с перепиской
327
+ превращается в «Нажмите «Сохранить» для продолжения», сообщение остаётся дословным.
328
+
329
+ ### Добавлено
330
+
331
+ - **Пакет говорит, когда проиграл язык другому пакету.** Сервис локали отдаёт язык тому, кто
332
+ зарегистрировал его первым, и второй русский пакет раньше терял свои словари молча.
333
+ Теперь в консоли появляется: «[dsh-client-locale-ru] язык ru уже занят другим пакетом; наши
334
+ словари не зарегистрированы в 81 namespace. В интерфейсе останется перевод другого пакета —
335
+ оставьте в профиле один пакет с русским языком.»
336
+ - **Отчёт о плагинах предупреждает о сопернике:** `npm run report:plugins` перечисляет
337
+ пакеты, которые сами претендуют на русский, с их версиями.
338
+ - **Публикация в CI идемпотентна.** Реестр отвечает на запрос с задержкой в минуты, и релиз,
339
+ вырезанный вручную и помеченный тегом, попадал в это окно: страж успевал решить, что
340
+ версии нет, а npm отказывался публиковать поверх существующей. Теперь страж спрашивает
341
+ трижды, а публикация после отказа перепроверяет реестр и заканчивается успешно.
342
+ - **Гейт формы workflow.** Сломанный workflow не падает громко: GitHub сообщает о прогоне по
343
+ имени файла, без единого job, а локальные гейты остаются зелёными. `npm run
344
+ check:workflow-yaml` проверяет отступы, `run:`/`uses:` у каждого шага и табуляции.
345
+
346
+ Цепочка verify выросла до 22 проверок.
347
+
348
+ ## [0.2.6] — 2026-10-19
349
+
350
+ ### Изменено
351
+
352
+ - **Описание пакета теперь содержит слова, по которым его ищут.** Замер поиска npm: пакет был
353
+ первым по запросу «dsh language pack», но не попадал в первые 250 результатов по «dsh i18n»
354
+ и «dsh russian» — у соседнего пакета эти слова есть в *имени* (`dsh-i18n`), а мы можем
355
+ совпасть только текстом. В описании появились «i18n» и «Russian (ru)», а в блок метаданных
356
+ каталога — «i18n».
357
+ - **В трёх README описана причина отказа установки.** Политика зрелости pnpm не ставит версии,
358
+ опубликованные меньше 48 часов назад: профиль отвечает `ERR_PNPM_NO_MATURE_MATCHING_VERSION`,
359
+ и это выглядит как поломка пакета. Теперь в разделе установки сказано, что это политика и
360
+ что с ней делать.
361
+
362
+ ## [0.2.5] — 2026-10-19
363
+
364
+ ### Исправлено
365
+
366
+ - **Workflow перестал быть нечитаемым для GitHub.** Шаг в обоих workflow был вставлен с
367
+ отступом `run:` вместо отступа шага, поэтому файл не разбирался: GitHub сообщал о прогоне
368
+ по имени файла, без единого job, а локальные гейты оставались зелёными. Отступ исправлен,
369
+ оба файла разбираются настоящим YAML-парсером.
370
+ - **Добавлен гейт `npm run check:workflow-yaml`:** заголовок, раздел `on:`, раздел `jobs`,
371
+ все шаги на одном уровне отступа, у каждого шага есть `run:` или `uses:`, никаких
372
+ табуляций. Проверен инъекцией — сдвинутый отступ шага валит гейт. Гейт вошёл в цепочку
373
+ verify (19 проверок) и в оба workflow.
374
+
375
+ Содержательно пакет не менялся: 81 namespace, 4949 строк. Выпуск нужен, чтобы тег, дерево
376
+ и реестр описывали одно и то же состояние.
377
+
378
+ ## [0.2.4] — 2026-10-19
379
+
380
+ ### Исправлено
381
+
382
+ - **Версия в блоке метаданных каталога** была на два выпуска позади: блок
383
+ `deepseek-harness-meta` в трёх README проверялся только релизным пайплайном, и локальные
384
+ гейты его не видели. Теперь проверка `npm run check:meta` стоит в цепочке verify и в обоих
385
+ workflow, а версия и заявленное число строк в блоке приведены к пакету.
386
+ - Выпуск 0.2.3 остаётся в реестре с устаревшей строкой версии в блоке: снять его нельзя —
387
+ granular-токен npm не выполняет unpublish. Каталог читает последнюю версию, поэтому
388
+ правильные метаданные — здесь.
389
+
390
+ ## [0.2.3] — 2026-10-19
391
+
392
+ ### Добавлено
393
+
394
+ - **Три чужих словаря, которые мы теперь тоже переводим.** Замер по реестру (поиск «dsh»,
395
+ 60 пакетов с манифестом `dsh`) показал: словари есть у 10 пакетов, мы покрывали 873 ключа
396
+ из 1417. Теперь закрыты все оставшиеся namespace:
397
+ - `dsh-remote-ssh` (@zhangfengshun/dsh-remote-ssh@2.4.33, MIT) — 111 ключей: профили
398
+ подключения, импорт из ~/.ssh/config, удалённые файлы, терминал, синхронизация;
399
+ - `univer` (dsh-univer-office@0.3.7, Apache-2.0) — 49 ключей: живой просмотр документов,
400
+ слияние и отклонение изменений, карточки проверки в разговоре;
401
+ - `web-ui-plugins` (@linxin666/dsh-client-ui-web-ui-settings@0.4.7, Apache-2.0) — 1 ключ.
402
+ - **Покрытие экосистемы: 1327 ключей из 1417 (94 %)** — против 873 (62 %) месяц назад.
403
+
404
+ ### Изменено
405
+
406
+ - **Подписи времени в codexUi** («{count} дн», «{count} нед») не склоняют существительное и
407
+ требовали форм числа, которых у плагина нет. Заменены нейтральной формулировкой
408
+ («дней: {count}», «недель: {count}»).
409
+ - **Семь имён собственных** Remote-SSH (glob, grep, node, sshpass, keyPath, nodePath,
410
+ remoteRoot) добавлены в список латиницы с указанием происхождения; «issue #23» переведено
411
+ в «проблема №23».
412
+
413
+ ### Исправлено
414
+
415
+ - `picker.errNameBad`: литералы «.» и «..» взяты в кавычки — пробел перед точкой был
416
+ дефектом типографики, а без них шаблон требовал закончить фразу двумя точками.
417
+
418
+ Инвентарь и заявленные числа обновлены: **81 namespace, 4949 строк**, пакет 493 КБ.
419
+
420
+ ## [0.2.2] — 2026-10-18
421
+
422
+ ### Изменено
423
+
424
+ - **Метаданные пакета переписаны под поиск.** Замер по реестру: по запросам «dsh language
425
+ pack», «dsh i18n», «dsh russian», «dsh locale» пакет не входил в первые 25 результатов, а
426
+ соседний языковой пакет стоял на первом-втором месте. Описание начиналось со слов, которых
427
+ в запросах нет, и не содержало фразы `dsh language pack`.
428
+ Теперь описание начинается с `dsh language pack — Russian (ru) locale…`, а ключевые слова
429
+ добавлены в трёх видах: служебные (`dsh-language-pack`, `dsh-locale`, `dsh-i18n`,
430
+ `dsh-russian`), человеческие (`language-pack`, `locale`, `russian`) и русские (`русский`,
431
+ `перевод`, `локализация`) — последние нашлись у соседнего пакета, и по ним он ранжировался
432
+ выше нас.
433
+ Код, словари и бандл не менялись: это выпуск метаданных.
434
+
435
+ ## [0.2.1] — 2026-10-14
436
+
437
+ ### Добавлено
438
+
439
+ - **Новая возможность: `npm run report:plugins`.** Отчёт об английском в собственной
440
+ установке: обходит `node_modules` профиля, находит в каждом плагине его словарь и
441
+ сравнивает ключи с нашим. Плагин без словаря называется так и есть — переводить нечего,
442
+ пока плагин не спросит перевод; плагин, который уже несёт свой русский, помечается
443
+ отдельно; «ждать нашей работы» считается отдельным числом, потому что это и есть
444
+ очередь. Отчёт читает и `--json`.
445
+ На живой установке он сразу дал результат: из девяти DSH-плагинов **семь не имеют
446
+ словаря вовсе** (`@cloudbase/dsh-plugin`, `@goodandready/dsh-live-canvas`,
447
+ `@local/dsh-quota-widget`, `dsh-figma-mcp`, `dsh-workbuddy-connect`, `superdesign-dsh`),
448
+ два наших (`@ragnoryok1/dsh-client-theme-pack` — 41 строка, `@ragnoryok1/dsh-plugin-doctor`
449
+ — 45 строк) уже двуязычные, и **ни один плагин не остался непокрытым**. Это и есть ответ
450
+ на вопрос «стоит ли переводить соседей»: переводить там нечего, пока они не начнут
451
+ спрашивать перевод.
452
+ - **Три слепых пятна разбора закрыты, и все три — в собственных пакетах.** Скрипт выгрузки
453
+ понимал только `locale.register(NS, { zh, en })`. Настоящий контракт допускает ещё три
454
+ формы: `register(NS, "en", en)` с словарём по имени (так устроен наш `plugin-doctor`),
455
+ `register(NS, { en: {…}, ru: {…} })` и `register(NS, { en, ru })` с обеими половинами по
456
+ имени (так устроен наш `theme-pack`). Пока правило знало одну форму, три собственных
457
+ плагина выглядели как «словаря нет» — ровно тот класс дефекта, что и дефис в ключе в
458
+ круге 5.
459
+ - **Тринадцатый словарь: `task-board-github` (113 строк).** Синхронизация задач GitHub:
460
+ настройка репозиториев и метки включения, учётные данные (токен уходит один раз на хост и
461
+ в браузер не возвращается), фоновая синхронизация с сообщениями о том, что она
462
+ остановилась, карточка PR с состояниями, «промпт выполнения» с шаблоном и неизменяемым
463
+ блоком происхождения, анализ задачи моделью и предупреждение, когда задача не требует
464
+ правок кода. Пакет вырос до **77 namespace и 4495 строк**.
465
+ - **Границы фронта выяснены: у конкурента 13 выгружаемых словарей, а не 16.**
466
+ `session-id` (0.1.1) вообще не содержит словаря — его текст зашит в бандл, `t(...)` там
467
+ нет, поэтому переводить в нём нечего; `settings.pluginManager` — это namespace **самого
468
+ харнесса**, и у нас он был переведён задолго до наступления. Оставшийся
469
+ `@linxin666/dsh-client-ui-plugin-manager` тоже пишет в настройки харнесса, поэтому своего
470
+ словаря не имеет.
471
+ - **Двенадцатый и самый крупный словарь: `task-board` (437 строк).** Доска задач: колонки
472
+ и группировка по времени, карточки с подзадачами и расписаниями cron, диалог создания с
473
+ разбором текста моделью, метки с подсказками для запуска, пакет передачи, замороженный
474
+ снимок, выполнение командой агентов и встроенным `/goal`, а также **отчёт о приёмке**:
475
+ критерии качества, порог, раунды A/B, недопустимые вердикты с причинами, аномалии,
476
+ очистка доказательств. Пакет вырос до **76 namespace и 4382 строк**.
477
+ Итог фронта: **все двенадцать выгруженных словарей семейства переведены** — 1349 строк,
478
+ у конкурента закрыто 12 namespace из 16. Проверки пополнились тремя решениями: имя
479
+ встроенного пресета `Cordis` объявлено в `keep-english.json`, `issue` в прозе заменён
480
+ русской формулировкой (это чужое слово для трекера), а `goalRun` — имя поля настройки, и
481
+ вместо него в тексте теперь говорится о флаге запуска через `/goal`. Собственная
482
+ неряшливость тоже найдена: «Не более {count} запусков» ломалось на единице, стало
483
+ «Предел запусков: {count}».
484
+ - **Одиннадцатый словарь: `dsh-ssh` (154 строки).** Панель удалённых операций:
485
+ хосты и группы, импорт из `~/.ssh/config` с причинами пропуска (блок `Match`, шаблон с
486
+ подстановкой, уже настроенный псевдоним), аутентификация ключом, агентом и паролем,
487
+ проверка подключения с задержкой, терминал с двухфакторной аутентификацией, передача
488
+ файлов и туннели. Пакет вырос до **75 namespace и 3945 строк**.
489
+ Проверки получили две записи в политику: имена потоков `Stdout`/`Stderr`, аббревиатура
490
+ `SSH` и директива `ProxyCommand` объявлены в `keep-english.json` (это литералы, а не
491
+ непереведённый текст), а `pageant`, `development`, `production` и `none` — разрешённые
492
+ латинские значения полей. Заодно убраны три собственные неряшливости: «{bytes} байт»
493
+ переписано как «Готово, байт: {bytes}» (иначе на единице получалось «1 байт» не по
494
+ форме), «блок Match» получил заглавную по шаблону, а адресная форма
495
+ `[user@]host[:port]` заменена русской формулировкой — англоязычный шаблон в подсказке
496
+ читался как непереведённый текст.
497
+ - **Девятый и десятый словари: `liangshen` (42 строки) и `dsh-web-ui-preset-center`
498
+ (46 строк).** Первый — режим LiangShen: рычаг, три состояния, предупреждения о том, что
499
+ режим фиксируется после старта сессии, роль диспетчера и представление инструментов в
500
+ запросе. Второй — центр пресетов: установка, включение, обновление, удаление, состояния
501
+ («id занят пресетом другого плагина», «изменён локально»), предупреждение о том, что
502
+ установка исполняемого пресета равна доступу к командной строке. Пакет вырос до
503
+ **74 namespace и 3791 строки**.
504
+ Здесь же три уточнения проверок: `settings.*` в чужих словарях сверяются по шаблону
505
+ **своего** namespace (иначе диалог «Установка исполняемого пресета» сравнивался с
506
+ вопросом `common · confirm.title` «Enable Full access?» и выглядел как потерянный
507
+ вопросительный знак); правило регистра стало односторонним — заглавная в шаблоне не
508
+ должна исчезать, а обратное нормально, потому что русское предложение начинается с
509
+ заглавной, даже если английское открывается строчным код-токеном
510
+ (`customSkillDirs config`, `v{version} release notes`); значение настройки `native`
511
+ объявлено в `keep-english.json` — переводить его нельзя, плагин сравнивает его как
512
+ литерал, поэтому в прозе оно заменено на «родной».
513
+ - **Восьмой и последний выгруженный словарь семейства: `dsh-web-ui-market`.** 106 строк
514
+ (`@linxin666/dsh-client-ui-market@0.4.7`) — мастерская: категории и подкатегории, скины,
515
+ питомцы, пресеты, установка одним нажатием, «выбор редакции», голоса и npm-загрузки.
516
+ Здесь особенно важна сборка из частей: `settings.descriptionPrefix` и `…Suffix`
517
+ склеиваются вокруг адреса сайта, поэтому по-русски порядок слов другой, а пробелы на
518
+ краях сохранены — иначе на стыке получилось бы «на и ставьте». Пакет вырос до
519
+ **72 namespace и 3703 строк**. Имя сайта `dsh-market.com` объявлено в
520
+ `keep-english.json`: это адрес, а не непереведённый текст.
521
+ Итог по фронту: **все восемь выгруженных словарей семейства переведены** (564 строки до
522
+ этого круга и 106 в нём — 670), у конкурента закрыто 8 namespace из 16; остальные его
523
+ плагины ещё не выгружены.
524
+ - **Седьмой словарь за пределами харнесса: `dsh-skill-explorer`.** 75 строк
525
+ (`@linxin666/dsh-client-ui-skill-explorer@0.4.7`, лицензия BSD-3-Clause — первая не
526
+ Apache в семействе) — центр навыков: список по группам (системные, проектные,
527
+ пользовательские, зарегистрированные во время работы), включение и выключение вызова
528
+ моделью, правка и удаление в `.trash`, создание навыка. Пакет вырос до **71 namespace и
529
+ 3597 строк**. Единственное новое слово в проверке латиницы — `customSkillDirs`, имя
530
+ параметра конфигурации, разрешено как имя параметра.
531
+ - **Шестой словарь за пределами харнесса: `remote`.** 115 строк
532
+ (`@linxin666/dsh-remote-web-ui@0.4.7`) — удалённый доступ: сопряжение телефона по QR,
533
+ публичный туннель Cloudflare, ретрансляция с постоянным именем хоста, доступ по локальной
534
+ сети с правилом брандмауэра, cookie устройства, отзыв сопряжений. Самый технический из
535
+ словарей семейства: половина строк — предупреждения о том, что канал /api открыт, что
536
+ привязка к 0.0.0.0 требует перезапуска и что за обратным прокси сопрягать надо токеном.
537
+ Пакет вырос до **70 namespace и 3522 строк**.
538
+ Проверка латиницы получила два уточнения: флаг (`--host`, `--token`) и слова внутри
539
+ скобок с именем команды (`(cloudflared tunnel run --token)`) — это инструкция, а не
540
+ проза; список имён команд закрытый, поэтому `tunnel` в обычном предложении по-прежнему
541
+ нарушение. Заодно `latinInValues` теперь спрашивается раньше структурных исключений:
542
+ иначе разрешение, чьё слово стало покрываться правилом команд, отчитывалось как
543
+ устаревшее, продолжая быть причиной разрешения. `cookie` разрешён как термин HTTP.
544
+ - **Пятый словарь за пределами харнесса: `pet`.** 81 строка (`@linxin666/dsh-pet@0.4.7`) —
545
+ питомец-компаньон: кормление, игра, магазин лакомств, ранги привязанности, Live2D и
546
+ форма настроек. Пакет вырос до **69 namespace и 3407 строк**.
547
+ Выгрузка вскрыла ещё три слепых пятна собственного разбора: `String#search` возвращает
548
+ `-1`, а не `null`, поэтому запасной путь для `var en =` не срабатывал и целых три
549
+ пакета семейства считались «без словаря»; namespace брался из любого `.register(` и один
550
+ раз оказался `market-like` (действие Turnstile, а не locale) — теперь он читается из
551
+ `locale.register(КОНСТАНТА, { zh, en })` с разрешением константы; наконец `\w` в JS —
552
+ ASCII-only, поэтому девять ключей с иероглифами (`pet.rank.name.伙伴`) вообще не
553
+ проверялись. После третьей правки сразу нашлись значения, которые до проверок не
554
+ доходили.
555
+ - **Четвёртый и самый большой словарь за пределами харнесса: `dsh-web-ui-session-archive`.**
556
+ 124 строки (`@linxin666/dsh-session-archive@0.4.7`) — менеджер архива сессий: автоархивация
557
+ и автоудаление с порогами, пакетные операции, предпросмотр, фильтры, сортировка, защита
558
+ запущенных сессий и «семейств». Пакет вырос до **68 namespace и 3326 строк**.
559
+ На этом словаре окупилась работа над разбором: **девять ключей с дефисом**
560
+ (`arch.issue.no-archive-time`, `arch.reason.family-protected`, …) парсер пакета молча
561
+ пропускал, поэтому они не проверялись вовсе. После расширения шаблона ключа до `[\w.-]`
562
+ нашлись ещё два незамеченных случая в словарях харнесса — путь `userData/keybindings.json`
563
+ (объявлен в `keep-english.json`) и `localStorage` в прозе (разрешён как имя API браузера).
564
+ Оба класса дефектов были невидимы ровно потому, что ключ не доходил до проверок.
565
+ - **Третий словарь за пределами харнесса: `dsh-web-ui-usage`.** 63 строки
566
+ (`@linxin666/dsh-usage@0.4.7`) — статистика расхода: баланс, карточка дня, тарифные
567
+ планы, пиковые и непиковые часы DeepSeek, счётчики токенов и «китовый юань» (купон,
568
+ который плагин рисует за каждый миллион токенов). Числа без согласования с
569
+ существительным, потому что ключи одиночные: «Запросов: {n}», «Китовых юаней
570
+ начислено: {minted}». Пакет вырос до **67 namespace и 3202 строк**.
571
+ - **Второй словарь за пределами харнесса: `git-graph`.** 63 строки
572
+ (`@linxin666/dsh-client-ui-git-graph@0.4.7`) — граф коммитов, создание и переключение
573
+ веток, дополнительные рабочие деревья: диалоги, ошибки Git («отслеживаемые файлы были бы
574
+ перезаписаны»), относительное время («5 мин назад»), тосты. Пакет вырос до **66
575
+ namespace и 3139 строк**; `update` и `git-graph` выгружены из собственных публикаций
576
+ плагинов и проверяются теми же правилами, что и харнесс.
577
+ Заодно ужесточена проверка латиницы: токен внутри пути (`$DSH_HOME/worktrees/`,
578
+ `feature/git-branch-switcher`) или в угловых скобках (`wt/<name>`) — часть примера,
579
+ который читатель вводит, а не английская проза; доказано, что проза в середине фразы
580
+ по-прежнему ловится («Ветка repository» → нарушение).
581
+ - **Первый словарь за пределами харнесса: `update`.** Пакет переводил только клиент
582
+ харнесса и его собственные плагины; сторонние плагины оставались английскими, потому что
583
+ их ключей нет в дереве и сверять не с чем. Теперь английский текст берётся из
584
+ **собственной публикации плагина**: `npm run fetch:plugins` скачивает пакет, выгружает
585
+ `en` из его бандла и записывает `plugins/<namespace>.json` с происхождением
586
+ (пакет, версия, лицензия). Аудит подхватывает эти словари и применяет к ним те же
587
+ правила: полнота по ключам, плейсхолдеры, типографика, латиница, написание названий.
588
+ Первый переведённый — `update` (43 строки, `@linxin666/dsh-update@0.4.7`): панель
589
+ обновлений целиком, включая команды pnpm и сообщения об ошибках. Выгружены также
590
+ `git-graph` (63), `dsh-web-ui-usage` (63) и `dsh-web-ui-session-archive` (124) — они
591
+ лежат в `plugins/` и ждут перевода, их видно в NAMESPACES.md.
592
+ Проверки получили три уточнения: мёртвыми не считаются ключи выгруженных плагинов (их
593
+ ссылки — в чужом пакете, а не в дереве); в `terminology.json` появился `latinInValues` —
594
+ слово, разрешённое только в конкретном значении (`install` и `enable` в команде
595
+ `npm install -g pnpm`), и разрешение, которое уже ничего не разрешает, валит сборку;
596
+ из `update.error.stale` убраны прямые кавычки вокруг примера `@linxin666/*`, потому что
597
+ кавычки-ёлочки в примере конфигурации велели бы вписать ёлочки в YAML.
598
+ - **Названия продуктов писались как получится.** «GitHub Desktop» с «Github» читается
599
+ разработчиком как опечатка в имени продукта, и появиться она может только у нас: в
600
+ английском шаблоне слово написано правильно. Новый гейт сверяет написание латинских
601
+ названий с английскими шаблонами харнесса: если харнесс пишет слово с большой буквы
602
+ ровно одним способом, наш вариант обязан совпасть. Два предохранителя от шума: слово,
603
+ которое харнесс пишет только строчными («cursor», «desktop»), не считается — там наша
604
+ заглавная как раз и означает имя продукта; слово, которое харнесс сам пишет двумя
605
+ способами, тоже пропускается. Сейчас расхождений 0 из 1497 латинских слов. Доказано
606
+ подстановкой: «GitHub Desktop» → «Github Desktop» даёт
607
+ «open-in-app · app.github: «Github», а харнесс пишет «GitHub»» и `exit 1`; вернуть —
608
+ чисто.
609
+ - **Блок команд в README показывал половину инструментов.** В нём было восемь строк из
610
+ пятнадцати: `check:namespaces`, `check:tarball`, `check:workflows`, `namespaces`,
611
+ `readmes:record` и `diff:harness` появились в последних кругах и не были перечислены —
612
+ читатель (и будущий сопровождающий) не мог узнать, что они есть. Блок переписан на трёх
613
+ языках: сначала проверки по одной, затем обслуживание отдельной группой (перегенерация
614
+ инвентаря, перезапись хешей README, сравнение двух деревьев харнесса). Структура трёх
615
+ README при этом не изменилась — гейт структуры и гейт пар прошли.
616
+ - **Три перевода отстали от переформулировок харнесса, и ни один гейт этого не видел.** Все
617
+ проверки отвечают на вопрос «полон ли пакет для одного дерева»; ни одна не отвечает на
618
+ «переформулировал ли харнесс то, что мы уже перевели». Ключ, у которого английский стал
619
+ «Could not display feature preview» вместо «Feature preview is unavailable», сохраняет наш
620
+ русский и сохраняет зелёный аудит — а читатель видит фразу, которой больше нет. Новый
621
+ инструмент `npm run diff:harness -- <старое дерево> <новое>` сравнивает английские
622
+ шаблоны двух деревьев и печатает рабочий список: переформулированные ключи вместе с нашим
623
+ переводом, новые строки, исчезнувшие строки. Сравнение `dsh-v0.2.1-alpha.3` →
624
+ `dsh-v0.2.1-rc.2` нашло **4** переформулировки, и три из них были настоящими ошибками:
625
+ `previewUnavailable` читался как «Предпросмотр возможности недоступен», хотя английский
626
+ сменился на «не удалось показать» (и «feature» — это «функция», а не «возможность»);
627
+ `baseUrlHint` описывал только «оставьте пустым», тогда как английский добавил префикс API
628
+ и `/messages`; `maxUsesHint` говорил о «сколько раз запрос может искать», а английский —
629
+ о вспомогательной модели и Messages API. Все три переписаны под текущий текст.
630
+ Четвёртая переформулировка (`{peers}` → `{requirements}`) была исправлена ещё при
631
+ синхронизации.
632
+ - **Проверялся репозиторий, а не то, что устанавливают.** Все проверки читают рабочее
633
+ дерево: `check:built` сравнивает `lib/` с HEAD, `check:manifest` обходит `exports` на
634
+ диске, `check:namespaces` грузит `lib/client.js` из чекаута. Сам архив при этом не
635
+ смотрел никто — а пользователь ставит именно его: файл, забытый в `files`, цель
636
+ `exports`, существующая только в сборке, или бандл, разошедшийся при упаковке, прошли
637
+ бы всё и провалились в единственный важный момент. Новый `check:tarball` пакует
638
+ пакет, распаковывает в стороне и задаёт те же вопросы архиву: каждая цель `exports` и
639
+ `dsh.bundle.patch` на месте, документация (`NAMESPACES.md`, README обоих языков,
640
+ CHANGELOG, LICENSE) едет с пакетом, а распакованный `lib/client.js` через
641
+ `namespaces.mjs --check --pack` воспроизводит проверенный инвентарь.
642
+ Доказано подстановкой: убрать `NAMESPACES.md` из `files` — «NAMESPACES.md обещан в
643
+ files, но в архиве его нет» и `exit 1`; вернуть — «опубликованный артефакт совпадает
644
+ с проверенным пакетом». Заодно `namespaces.mjs` получил флаг `--pack`, чтобы вопрос
645
+ «что в бандле» можно было задать любому бандлу, а не только чекауту.
646
+ - **Выпуск мог уйти с историей предыдущей версии.** `npm publish` не смотрит в
647
+ CHANGELOG: страница пакета в реестре показывает тот раздел, который там лежит, и
648
+ релиз без раздела для своей версии описывал бы предыдущий. Теперь это гейт
649
+ (`check:changelog`, в `verify` и обоих workflow): верхний раздел обязан называть
650
+ версию из `package.json`, дата — `YYYY-MM-DD` и не из будущего, в разделе есть хотя
651
+ бы один пункт, а версии идут по убыванию (вставка посреди истории видна сразу).
652
+ Доказано подстановкой: поднять версию в `package.json` до 0.2.2 при CHANGELOG на
653
+ 0.2.1 — «верхний раздел описывает 0.2.1, а package.json — 0.2.2; выпуск ушёл бы со
654
+ старой историей» и `exit 1`; вернуть — чисто.
655
+ - **Релизный конвейер отставал от цепочки проверок, а его аудит смотрел не на тот
656
+ тег.** В `publish.yml` не было трёх новых проверок — `check:namespaces`,
657
+ `check:readmes:pairs` и матрицы совместимости, — то есть выпуск мог уйти с устаревшим
658
+ инвентарём и разошедшимися README. Хуже: шаг аудита в нём был закреплён на
659
+ `dsh-v0.2.1-alpha.3`, хотя пакет уже полгода сверяется с `dsh-v0.2.1-rc.2` — релиз
660
+ проверялся бы против чужого дерева. Оба места исправлены, матрица совместимости
661
+ добавлена и в `verify.yml` (раньше она жила только в локальном прогоне — в круге 53
662
+ шаг не появился из-за CRLF, и я это заметил только сейчас).
663
+ - **Новый гейт: workflow запускают то же, что `verify`.** `check:workflows` читает
664
+ `package.json` и оба workflow и валит сборку, если проверка из цепочки не запускается
665
+ в CI, если CI запускает что-то вне цепочки и вне явного списка CI-исключений
666
+ (`build` и `check:compat` — единственные, кому нужен особый режим) и если
667
+ `check:compat` не покрывает языки, формы и фразы транзитивно в релизном пути.
668
+ Доказано подстановкой: убрать шаг `check:namespaces` из `verify.yml` —
669
+ «verify.yml не запускает «check:namespaces» из цепочки verify» и `exit 1`.
670
+ - **Новый гейт: закреплённый тег совпадает во всех workflow.** Аудит теперь собирает
671
+ `HARNESS_TAG` из каждого файла `.github/workflows/*.yml` и требует равенства с
672
+ `verify.yml`. Доказано подстановкой: вернуть в `publish.yml` `alpha.3` —
673
+ «publish.yml: HARNESS_TAG dsh-v0.2.1-alpha.3, а закреплён dsh-v0.2.1-rc.2» и `exit 1`.
674
+ - **Обещание совместимости проверялось не полностью и не в CI.** `check:compat` скачивает
675
+ рантайм локали каждой из 12 заявленных версий и прогоняет против него два набора — языки и
676
+ формы числа. Собранные фразы не прогонялись, а ломается именно этот слой: `time.locale` и
677
+ категории форм приходят от рантайма, и версия, которая резолвит ключ иначе, выдаёт «Раз в 2
678
+ дня» из правильных по отдельности кусков. Третий набор добавлен в матрицу, и она теперь
679
+ входит в CI, а не только в локальный прогон: обещание в meta-блоке проверяется на каждом
680
+ пуше. Итог: **12 версий × 3 набора**, все зелёные. Доказано подстановкой: испортить шаблон
681
+ `frequency.weekly` на «По {weekdays} …» — `actual: 'По Пн, Ср в 09:00 (Europe/Moscow)'`,
682
+ `expected: 'Пн, Ср в 09:00 (Europe/Moscow)'` и `exit 1`; вернуть — 6/6.
683
+ - **Три README теперь не могут разойтись молча.** `check:readmes` сравнивает структуру
684
+ (заголовки, таблицы, картинки, абзацы), и именно поэтому правка круга 49 прошла незаметно:
685
+ в русском README стояло «пять случаев», в английском — то же устаревшее число, а скрипт
686
+ знал шесть; структура при этом совпадала полностью. Харнесс решает ту же задачу для своей
687
+ документации записанным хешем на раздел (`README.i18n.yaml`): правишь одну сторону пары —
688
+ запись ломается. Здесь это сделано для трёх языков и с привязкой к позиции раздела
689
+ (`scripts/readme-pairs.json`, 15 разделов × 3 языка): структура уже гарантирует одинаковую
690
+ форму файлов, поэтому раздел 4 — это «Проверки» и «Checks» одновременно. `npm run
691
+ readmes:record` перезаписывает запись после синхронной правки, `npm run check:readmes:pairs`
692
+ (в `verify` и CI) валит сборку при расхождении и называет, какой язык изменён, а какой нет.
693
+ Доказано подстановкой: добавить слово в заголовок русского README — «раздел 4 «Проверки»:
694
+ изменён ru, а en, zh — нет» и `exit 1`; вернуть — чисто. Заодно скрипт не считает
695
+ `# комментарий` внутри блока кода за заголовок, а раздел без текста (например, блок мета
696
+ до первого заголовка) не хешируется как пустая строка.
697
+ - **Подпись не помещалась в боковую панель — это видно на нашем же скриншоте.** На снимке
698
+ экрана настроек 0.1.8 пункт называется «Встроенные пл…»: 18 символов «Встроенные плагины»
699
+ не влезли туда, где «Built-in plugins» и「内置插件」влезают. Ширину колонки задаёт харнесс, а
700
+ не мы, поэтому правка на нашей стороне: подпись пункта стала «Встроенные» (10 символов, тот
701
+ же термин, что и в `builtInGroup`), а заголовок страницы остался полным — «Встроенные
702
+ плагины», у него место есть. Новый гейт `подписи в боковой панели настроек длиннее 16
703
+ символов` держит правило: подпись `nav` в namespace `settings` не может быть длиннее 16
704
+ символов. Доказано подстановкой: вернуть «Встроенные плагины» — `settings.plugins · nav —
705
+ 18 символов: «Встроенные плагины»` и `exit 1`; вернуть короткую — чисто.
706
+ - **Ссылка в README вела на файл, которого не было в публикации.** Инвентарь
707
+ `NAMESPACES.md` появился в репозитории в круге 45 и на него ссылаются все три README, но в
708
+ `package.json` `files` его не было: npm показывает README на странице пакета, и читатель
709
+ опубликованной страницы уходил по ссылке в файл, которого в архиве нет. Теперь в архив
710
+ попадают и `NAMESPACES.md`, и языковые варианты README (16 файлов вместо 15, 409 КБ), а
711
+ `check:manifest` проверяет это правило: каждая относительная ссылка из README должна
712
+ существовать в репозитории **и** покрываться `files`. Доказано подстановкой: убрать
713
+ `NAMESPACES.md` из `files` — три ошибки («ссылка на NAMESPACES.md, которого нет в
714
+ package.json files — он не попадёт в публикацию») и `exit 1`; вернуть — чисто. Заодно
715
+ проверяется, что обещанный в README размер и число файлов совпадают с архивом.
716
+ - **Документация перестала обещать числа, которые стареют.** README говорил «`check:plural`
717
+ — пять случаев», хотя случаев шесть: круг 44 добавил проверку чужого языкового пакета, а
718
+ фраза осталась. Ещё одна фраза утверждала «гейтом проверяются шесть групп проверок» — к
719
+ этому моменту групп стало больше двадцати (полнота, формы числа, плейсхолдеры, остатки,
720
+ терминология, латиница, гигиена, краевые пробелы, стиль, типографика значений, «ё»,
721
+ составные фразы, заявленные числа). Оба места переписаны так, что перечисляют состав, а не
722
+ количество: «на счетах 0, 1, 2, 3, 5 и 22, на ключе без пары, на дробном числе и на чужом
723
+ языковом пакете» и полный список гейтов. Число «18 правил типографики», «6 проверок
724
+ сосуществования словарей» и «jsdom, шесть случаев» сверены со скриптами и верны — они и
725
+ остались числами. Заодно убрано случайное одинокое слово «групп» на отдельной строке,
726
+ оставшееся от прошлой правки.
727
+ - **Одно слово — одно написание «ё».** Пакет, где рядом стоят «ещё» и «еще», читается как
728
+ машинный перевод, хотя оба варианта по отдельности допустимы. Новая проверка сводит «ё» к
729
+ «е» и валит сборку, если одна форма пишется двумя способами; сейчас таких пар 0 из 2494
730
+ разных слов. Настоящие разные слова («все» и «всё») объявлены в `scripts/yo-pairs.json`, а
731
+ запись, чьи написания из пакета ушли, тоже валит сборку — иначе список решений тихо
732
+ расширял бы проверку. Доказано подстановкой: «Ещё» → «Еще» в одном ключе дало
733
+ «еще: еще×1, ещё×49» и `exit 1`; выдуманная пара в `yo-pairs.json` — «объявления
734
+ yo-pairs.json, которых больше нет в паке: 1» и `exit 1`; обе правки откачены.
735
+ - **Словари плагинов впервые прочитаны как текст.** Пока аудит не видел их namespace, 372
736
+ строки не проверял никто, кроме меня глазами при переводе; теперь гейты их видят, и
737
+ механических находок не было — но чтение всё равно дало шесть правок. `press.stale` в
738
+ `claude-code-mods` переводил «drawing» как «рисунок» («Эта кнопка относилась к предыдущему
739
+ рисунку») — в интерфейсе это отрисовка: «Эта кнопка осталась от прошлой отрисовки; полоса
740
+ обновилась». `press.failed` переставлен в естественный порядок: «Кнопка не сработала».
741
+ В `session-inspector` «Наведите в основном чате» стало «Наведите курсор в основном чате» —
742
+ без курсора фраза читается как «наведите что-то». В `voice-input` «Заложите около 4 ГБ»
743
+ заменено на «Планируйте около 4 ГБ» (закладывают деньги, а не место), «в текущую позицию
744
+ курсора» сокращено до «в позицию курсора», а «Попробуйте записать короче» — на «Сделайте её
745
+ короче»: ограничение службы не про попытку.
746
+ - **Аудит нашёл настоящую причину «мёртвых» namespace — и она была в его собственных
747
+ шаблонах.** Идентификатор namespace записывается в кавычках, и три регулярных выражения
748
+ ловили его как `[\w.]+` — без дефиса. Поэтому `open-in-app`, `voice-input`,
749
+ `session-log-download`, `session-inspector`, `claude-code-mods`, `agent-team` и
750
+ `deepseek-model-label-1017` не связывались со своими словарями: аудит видел имя, но не
751
+ ключи. После правки харнесс описывается **67 namespace** вместо 59 (это пакеты самого
752
+ дерева — плагины из `client/`, `experimental/`, `session-query/`), и все семь словарей
753
+ проверяются как обычные: привязка, ключи, плейсхолдеры, остатки.
754
+ - **Привязка namespace к словарю теперь идёт через импорт.** `locales.ts` экспортирует
755
+ `NS = 'open-in-app'` рядом со своими блоками `zh`/`en`, а `index.ts` делает
756
+ `locale.register(NS, { zh, en })` — имя приходит из другого файла, и правило «константа и
757
+ регистрация в одном файле» не срабатывало. Теперь идентификатор разрешается через импорт;
758
+ заодно обрабатывается литеральная форма `locale.register('settings.permission', { zh, en })`,
759
+ которая раньше объявляла namespace, но ничего к нему не привязывала.
760
+ - **Убрано скрещивание словарей.** Модуль, владеющий двумя namespace
761
+ (`ui-schedule/index.ts` — `schedule.catalog` и `schedule.manager`), привязывал **каждый**
762
+ импортированный словарь к **каждому** своему namespace: 127 ключей менеджера попадали в
763
+ каталог и выглядели как дыра в переводе. Привязка по импорту включается только при одном
764
+ владельце — когда двусмысленности нет.
765
+ - **Namespace-константа считается локалью только там, где её потребляет локаль.** Правило «имя
766
+ содержит `NS`» ловило и служебные идентификаторы: `agent-preset-registry` — это хранилище
767
+ настроек, `llm-pi-ai` — форма конфигурации провайдера, `session-log-deepseek` — канал
768
+ remote. Все три требовали переводов, которые уже есть в своих настоящих namespace (166
769
+ ключей). Теперь константа объявляет namespace, только если рядом есть
770
+ `locale.register(IDENT…)` или `locale.bind(IDENT)`.
771
+ - **Политика `scripts/plugin-namespaces.json` удалена.** Она была обходным путём: пока сканер
772
+ не видел дефис, приходилось объявлять плагинные словари вручную и освобождать их от сверки
773
+ с шаблонами. Теперь они — обычные namespace харнесса со своими шаблонами, и 31 «английская
774
+ копия» в них (названия приложений вроде «Cursor», «VS Code Insiders», служба «Hugging
775
+ Face») объявляется так же, как всё остальное: записью в `keep-english.json`, включая
776
+ закрытый список `open-in-app · app.*`.
777
+ - **Аудит годами не видел семь namespace — и это были все словари плагинов.** Регулярное
778
+ выражение, которым аудит вырезал заголовки namespace из `dicts.ts`, было записано как
779
+ `[\w.]+`, а дефис в него не входит: `agent-team`, `voice-input`, `claude-code-mods`,
780
+ `session-inspector`, `session-log-download`, `open-in-app` и `deepseek-model-label-1017`
781
+ молча выпадали из **всех** проверок — 372 строки (12% пакета) не сверялись ни с шаблонами,
782
+ ни на плейсхолдеры, ни на остатки, а заявленные числа (57 namespace, 2661 строка) были
783
+ меньше того, что пакет на самом деле регистрирует: **64 namespace, 3033 строки**. Проверено
784
+ подстановкой: дефект, добавленный в `agent-team` (лишние пробелы и кавычки), поймался
785
+ гигиеной — значит правила по тексту работали, а по namespace-привязке нет.
786
+ - **Словари плагинов объявлены, а не угадываются.** Харнесс эти namespace не объявляет,
787
+ поэтому сверка «мёртвых» считала их мусором, а сверка остатков сравнивала их значения с
788
+ чужими шаблонами по одному имени ключа (`title`, `close`, `cancel` встречаются в сотне
789
+ namespace) — оттуда 32 ложных «английских копии». Теперь есть
790
+ `scripts/plugin-namespaces.json`: объявленный словарь плагина проходит обе проверки, а
791
+ необъявленный по-прежнему валит сборку, поэтому опечатка в имени не проедет. Для словарей
792
+ плагинов осталось одно правило, которое не требует шаблона: фраза из трёх и более латинских
793
+ слов без кириллицы — это непереведённый текст, а односложные названия («Cursor», «Finder»,
794
+ «VS Code Insiders») остаются как есть и объявляются решением.
795
+ - **Переезды ключей между namespace тоже ловятся.** Записи политики `chat · app.*` и
796
+ `pluginManager · sourceHuggingFace` стали устаревшими не потому, что строки перевели, а
797
+ потому что харнесс перенёс ключи: приложения переехали в `open-in-app`, `sourceHuggingFace`
798
+ — в `voice-input`. Гейт устаревших решений показал обе записи, они переименованы.
799
+ - **Инвентарь перевода стал проверяемым документом.** `NAMESPACES.md` генерируется из
800
+ собранного бандла (`npm run namespaces`) и проверяется `check:namespaces` в `verify` и CI:
801
+ 64 namespace, 3033 строки, у словарей плагинов — пометка. Подпись каждой строки — заголовок
802
+ самого namespace, то есть текст, который читает пользователь, а не второе рукописное
803
+ описание. Из того же генератора берётся `scripts/pack-count.json` — **один** авторитетный
804
+ счётчик для README, бейджа и аудита: раньше аудит считал уникальные имена ключей (2661), а
805
+ пакет содержит 3033 значения, и два честных числа выглядели как расхождение.
806
+ - **Три строки каталога плагинов перечитаны после перевода.** «Сменить каталог плагинов,
807
+ сейчас {market}» — канцелярит с запятой; теперь «Сменить каталог плагинов (сейчас
808
+ {market})». «…устанавливать его плагины здесь» — теперь «…устанавливать плагины прямо
809
+ здесь». Статус версии «С исключением» читался как канцелярская пометка; теперь «Есть
810
+ исключение» — это утверждение о версии, а не о её состоянии.
811
+
812
+ - **Пакет догнал харнесс: закреплённая версия поднята с `dsh-v0.2.1-alpha.3` до
813
+ `dsh-v0.2.1-rc.2`.** Пока пакет стоял на alpha.3, вышел alpha.4, alpha.5 и два кандидата в
814
+ релиз; на rc.2 в клиенте появился новый экран — каталог плагинов (`pluginMarket`, 78
815
+ строк: индексы, доверие источникам, карточка плагина, версии, совместимость) — и 30 строк в
816
+ существующих namespace: разделы плагинов, смена версии службы (`settings.hostRelease`),
817
+ уровни рассуждений (`settings.models`) и новый API веб-поиска (`settings.webSearch`).
818
+ Переведены все: покрытие ключей харнесса — **2557 из 2557**, namespace — 57.
819
+ Заодно `reasonIncompatibleVersion` переведён под новые плейсхолдеры (`{requirements}` вместо
820
+ `{peers}` — харнесс переименовал параметр, и старый перевод потерял бы подстановку).
821
+ Диапазон совместимости проверен по-настоящему: `check:peers` и `check:compat` скачивают все
822
+ 12 версий из `compatible_versions` и прогоняют оба набора — 12 из 12 проходят.
823
+ - **Найден и исправлен ложный namespace в самом аудите.** Сканер искал константы по
824
+ подстроке «NS», и `INSTALLED_TAB = 'installed'` (I + NS + TALLED) превратилась в
825
+ namespace `installed`, которому аудит приписал 221 ключ из словаря менеджера плагинов. На
826
+ alpha.3 этого не было видно: там такой константы не было, а на rc.2 «непокрытый namespace»
827
+ с 221 ключом выглядел как огромная дыра в переводе. Теперь «NS» — сегмент имени
828
+ (`NS`, `COMMON_NS`, `SETTINGS_NS`), а не случайные буквы: 66 «namespace» стали 59
829
+ настоящими. Доказательство: после правки исчез ровно `installed`, а `pluginMarket`
830
+ остался — он объявлен как `const NS = 'pluginMarket'` и регистрируется по-настоящему.
831
+ - **Переведён и единственный namespace-«заглушка» харнесса** — `deepseek-model-label-1017`
832
+ с ключом `badge.label: 1017` (число параметров модели, не текст): без него покрытие было
833
+ бы 2556 из 2557.
834
+ - **Отчёт полноты по namespace.** Глобальная сверка имён ключей не видит дыру, когда перевод
835
+ лежит под чужим namespace: строка считается покрытой, а экран показывает английский. Аудит
836
+ теперь восстанавливает связь «namespace → словарь» по тому, кто его регистрирует (`NS` или
837
+ литерал в `locale.register`; `bind` и `PropsLocale` только читают namespace) и печатает
838
+ такие ключи вместе с английским шаблоном. Отчёт нашёл **49 ключей в 6 namespace**, и все
839
+ они разобраны: 45 перенесены из namespace, который обслуживает тот же словарь
840
+ (`subagent` ← `settings.subagent`, `schedule.manager` ← `schedule.catalog`, `job` ←
841
+ `conversation`), а 4 переведены заново по контексту — `model.search.placeholder`
842
+ («Поиск моделей…» вместо общего «Поиск…»), `inspectorPanel.title`/`toggle`
843
+ («Инспектор Node.js») и `question.error.unavailable`. На закреплённом теге отчёт теперь
844
+ пуст. Он остаётся **справочным**, а не гейтом: связь «namespace → словарь»
845
+ восстанавливается по шаблонам регистрации, и эвристика, способная выдумать расхождение,
846
+ не должна останавливать выпуск.
847
+ - **Гейт остатков: английский, китайский, пустые.** Значение обязано быть русским — аудит
848
+ сверяет каждое с английскими шаблонами ключа и валит сборку на точной копии английского,
849
+ китайском иероглифе или пустой строке там, где шаблон не пуст. Копии английского
850
+ допускаются только по решению из `scripts/keep-english.json` (адреса, форматные строки без
851
+ слов, названия протоколов и инструментов, аббревиатуры) — новая недопереведённая строка не
852
+ проедет молча. Проверка сразу нашла два упущения: подписи траектории `kind.*` (`SYSTEM`,
853
+ `USER`, `ASSISTANT`, `TOOL`, `SUBTOOL`, `Sub`, `CONTEXT`, `COMPACTED` оставались
854
+ английскими, хотя китайский словарь их переводит, а `kind.message` уже был русским) и
855
+ заголовок окна инспектора `NodeJS Inspector`. Ещё три строки — `Finder`, `Hugging Face` и
856
+ `JSON` — оказались в `common`, куда прежняя версия проверки не заглядывала, и объявлены в
857
+ политике как названия продуктов и аббревиатуры.
858
+ - **Мёртвые ключи удалены, и это теперь гейт.** Ключ, которого нет ни в одном словаре
859
+ харнесса и на который не ссылается ни один его исходник, — остаток от прежней версии: он
860
+ ничего не показывает и только путает. Таких нашлось 25 — старые `settings.theme.fontSize.*`,
861
+ которые харнесс переименовал в `fontSize.text|code|terminal.*`, ключи `pluginManager`,
862
+ `settings.agentPreset`, `schedule.catalog.status.*`, `workspace.defaultWorkspace.title`
863
+ и `model.option.deepseekV4*`. Все удалены: в пакете **2498 переводов вместо 2523**. Аудит
864
+ валит сборку на новом таком ключе, а те, что компонент всё ещё спрашивает, оставляет в
865
+ справочном отчёте со счётчиком ссылок: для них наше значение — единственное, что попадает
866
+ на экран.
867
+ - **Гейт терминологии: одно понятие — один перевод.** Ни одна проверка не видела, что одно и
868
+ то же слово переведено двумя способами: обе строки русские и обе на месте. Теперь
869
+ запрещённые варианты («кеш» вместо «кэш», «сеанс» вместо «сессия», «этап» вместо «шаг»,
870
+ а также «юзер», «ретрай», «апдейт», «сабмит», «дропдаун», «чекбокс» и другие) валят
871
+ сборку, а `scripts/terminology.json` хранит и решение, и счётчики выбранных форм. Проверка
872
+ сразу нашла два дефекта: «некешированный» в `cotTranslation.paidNotice` и ключ
873
+ `confirm.description`, переведённый в `settings.permission` как «шаги подтверждения»,
874
+ а в `permission.access` — как «этапы»; там же исправлено «Этапы подготовки» в голосовом
875
+ вводе.
876
+ - **Латиница внутри русских фраз — под проверкой.** Остатки ловят только значение целиком
877
+ на английском, а «компоненты этого bundle» — это середина фразы, и её не видел никто.
878
+ Аудит разбирает русские значения на слова и сообщает о строчных латинских словах вне
879
+ списка `terminology.json`; в списке 44 записи с причиной — имя продукта, команда,
880
+ код-токен, схема адреса, имя настройки, путь в примере. Разбор показал, что реальных
881
+ дефектов в пакете нет: вся латиница — это `DSH`, `JSON`, `Bash`, `Esc`, адреса, `pnpm`,
882
+ имена слэш-команд и ключи конфигурации. Проверка нужна, чтобы новый английский оборот
883
+ в предложении не проехал незамеченным.
884
+ - **Короткие слова больше не отрываются переносом.** К экранной типографике добавлено правило:
885
+ предлог, союз или частица («в», «и», «с», «на», «не», «ли», «же» и ещё два десятка) держат
886
+ следующее слово неразрывным пробелом, поэтому строка не может закончиться на висящем «в».
887
+ Слово прикрепляется только к русскому слову — в смешанном тексте «и option» не трогается, —
888
+ и цепочка обрабатывается целиком («и в тексте» остаётся неразрывной). Проверок стало 18,
889
+ включая случаи «не трогать»: cron-выражение, версии, латиница, части слов. Прогон по всем
890
+ значениям пакета: типографика меняет 674 строки из 2839, подозрительных — ноль.
891
+ - **Правило числовых диапазонов сознательно не добавлено.** Русская типографика пишет
892
+ диапазоны средним тире («5–10»), но единственная строка в пакете с дефисом между числами —
893
+ пример cron-выражения `0 9 * * 1-5`, и тире его испортило бы. Это измеренный вывод, поэтому
894
+ в тестах появился случай, закрепляющий обратное: cron-выражение обязано остаться как есть.
895
+ - **Проверка стиля против шаблона своего namespace.** Значение может быть переставлено
896
+ по-русски («Напоминаний: {count}» вместо «{count} reminders»), но знак в конце терять
897
+ нельзя, а регистр первой буквы обязан совпадать. Проверка сразу нашла `registryCustomHint`
898
+ без точки там, где английский её ставит. Два уточнения, без которых она врала: регистр
899
+ сверяется только когда шаблон не начинается с плейсхолдера и наше значение не начинается
900
+ с сокращения (`JSON параметров` против `parameters JSON` — законная перестановка), а
901
+ знаки сверяются в одну сторону — потерянный сообщается, добавленное русское «…» у статуса
902
+ «в процессе» нет. Заодно шаблоны декодируются из escape-последовательностей: исходники
903
+ пишут `'Creating\u2026'`, и сравнение по сырому тексту считало такое значение кончающимся
904
+ на цифру. Нашлась и дыра в самих проверках: файл-агрегат `locales/index.ts` не объявляет
905
+ `en`, поэтому весь namespace `common` молча выпадал из сверки стиля и краевых пробелов —
906
+ теперь при единственном английском варианте ключа шаблон берётся из общего набора.
907
+ - **Дозор за новыми тегами харнесса.** `watch-upstream` раз в неделю сравнивает закреплённый
908
+ тег с тегами харнесса: при новом открывает issue со списком шагов (выгрузить дерево,
909
+ прогнать аудит, обновить `HARNESS_TAG`, счётчики и CHANGELOG) и красит прогон, чтобы
910
+ отставание находилось по расписанию, а не по чужому скриншоту. Сравнение версий проверяется
911
+ без сети — `npm run check:upstream`, пять случаев, входит в `verify`; самопроверка сразу
912
+ поймала ошибку в компараторе, где релиз `0.2.1` считался старше своего предрелиза
913
+ `0.2.1-alpha.3`, и дозор после первого стабильного выпуска срабатывал бы вечно. Заодно
914
+ зафиксировано решение про два замка: канонический — `package-lock.json` (CI ставит им),
915
+ `pnpm-lock.yaml` объявлен локальным артефактом в `.gitignore`, а не удалён.
916
+ - **Русские формы не только у парных ключей.** Харнесс передаёт число и ключам без пары
917
+ (`terminal.expand` с `{n}`), и там форма выбиралась одна на все случаи: «Показать ещё 1
918
+ строк» вместо «1 строку». Обёртка над `translate` теперь ищет форму вида `<ключ>.<форма>` и
919
+ для таких ключей, пакет несёт для них свои `.one` / `.few` / `.many` (три ключа), а счётчик
920
+ читается не только из `count`/`n`, но и из первого числового параметра — харнесс называет
921
+ счётчики по экрану (`{shown}`, `{turns}`, `{pending}`). Форма подставляется только если она
922
+ есть в словаре, поэтому неверно прочитанное число не может породить перевод. Там, где форма
923
+ неуместна — в предложении несколько чисел, — значение переписано нейтрально к числу:
924
+ «Символов: {total}» вместо «{total} символов», «файлов: {files}» вместо «{files} файлов»,
925
+ «Путей: {shown} из {total}», «{bytes} Б» вместо «{bytes} байт», «{pending} в ожидании»
926
+ вместо «{pending} ожидает», «Ходов: {turns}, шагов: {steps}». Всего исправлено 13 строк,
927
+ аудит получил отчёт, который перечисляет оставшиеся места «число + склоняемое слово»
928
+ (с документированным списком нейтральных слов в `terminology.json`), а тестов форм стало
929
+ пять — добавлен случай с ключом без пары и с дробным числом.
930
+ - **Исправлено: типографика не применялась к тексту, который меняется.** Узел, однажды
931
+ обработанный, навсегда помечался готовым, поэтому живые значения — счётчики токенов,
932
+ таймеры, прогресс — теряли неразрывный пробел при обновлении: «за 5 ч» превращалось в
933
+ «за 4 ч» с обычным пробелом, и число могло оторваться от единицы. Теперь при изменении
934
+ текста метка снимается и проход повторяется; цикла нет, потому что запись происходит только
935
+ при реальном отличии результата. Дефект закрыт тестом на настоящем DOM (jsdom,
936
+ `npm run check:typography:dom`, четыре случая: первый проход, изменение текста, добавленное
937
+ поддерево и `code`, остановка при выгрузке). Тест доказан откатом: без исправления он падает
938
+ ровно на этом случае — `'за 4 ч'` вместо `'за 4\u00A0ч'`.
939
+ - **Подписи скриншотов в README уточнены.** Снимки сделаны в 0.1.8, и сверка строк показала,
940
+ что формулировки с тех пор изменились (например, «Влияет только на содержимое беседы» →
941
+ «…диалога»); подпись теперь говорит это прямо, чтобы установленный пакет не расходился с
942
+ картинкой незаметно.
943
+ - **Исправлено: типографика работала при любом языке интерфейса.** Проход включался сразу при
944
+ установке пакета, без проверки языка, а правила у него русские: в английском интерфейсе
945
+ `"quotes"` превращались в «quotes», а дефис между словами — в длинное тире, то есть пакет
946
+ правил текст, который ему не принадлежит. Теперь проход запускается только пока активен
947
+ русский и следует за переключением через событие `locale/change` (оно срабатывает на смену
948
+ языка и не срабатывает на регистрацию словаря), а проверка языка выполняется перед каждой
949
+ правкой, поэтому смена языка действует со следующей отрисовки. Заодно метки обработанных
950
+ узлов стали локальными для запуска, а не общими на модуль. Тест на настоящем документе
951
+ вырос до шести случаев (чужой язык и переключение языка — новые), тест сосуществования
952
+ языков — до семи; доказано откатом: без подписки тест падает на «слушателей 0, ожидался 1»,
953
+ без предиката — на неизменённом английском тексте.
954
+ - **Аудит читает ещё два вида значений харнесса.** Часть словарей хранит текст
955
+ **массивами абзацев** (руководства режимов) или **ссылкой на константу**
956
+ (`'hint.plan': PLAN_NEXT_ACTION_EN`), и такие ключи выглядели как «шаблона нет» — а
957
+ значит, не сверялись ни плейсхолдеры, ни краевые пробелы, ни стиль. Теперь парсер
958
+ разворачивает оба вида: сверка плейсхолдеров выросла с 2565 до **2575 ключей**, и под неё
959
+ попали восемь руководств целиком. Рядом появились стражи самой политики: пустой
960
+ `keep-english.json` или пустой раздел `terminology.json` теперь валят аудит (`exit 2`)
961
+ вместо того, чтобы молча ослабить гейт.
962
+ - **Числа, которые README заявляют о пакете, теперь сверяются с пакетом.** Бейдж
963
+ (`strings-2%2C535`), описание в meta-блоке и строка в тексте — три написания одного факта,
964
+ а публикующий workflow проверял из этого блока только версию. Счётчик разошёлся незаметно:
965
+ после добавления форм числа в пакете стало 2535 строк, а README продолжали обещать 2526 —
966
+ то есть ровно то число, которое читатель сравнивает между паками. Аудит читает все три
967
+ написания во всех трёх README и валит сборку на расхождении (сверка сразу нашла 9 мест).
968
+ Проверка структуры README получила и **число абзацев**: абзац, добавленный только в один
969
+ язык, дважды проходил незамеченным, потому что заголовки, блоки кода, таблицы и картинки
970
+ оставались теми же. Считаются не строки, а абзацы — перевод переносится по-своему, но
971
+ пропасть не должен. Доказано удалением абзаца: 70 против 71, `exit 1`.
972
+ - **Обещание совместимости проверяется, а не подразумевается.** В meta-блоке перечислены восемь
973
+ версий харнесса, с которыми пакет работает, но проверялся только закреплённый тег: API, который
974
+ есть в нём, мог отсутствовать в старых. Новый `npm run check:compat` берёт список оттуда,
975
+ скачивает рантайм локали каждой версии с npm и прогоняет против него оба набора —
976
+ сосуществование языков и формы числа. Все восемь проходят (7 + 5 тестов на каждой версии,
977
+ включая 0.1.7-alpha.2). Проверка добавлена в релизный workflow как отдельный шаг: обещание
978
+ проверяется в тот момент, когда его дают. Отказы тоже проверены — несуществующая версия даёт
979
+ «версии нет в реестре, проверьте compatible_versions в README», а сломанный бандл валит оба
980
+ набора на каждой версии.
981
+ - **Объявление совместимости проверяется по правилу харнесса.** Харнесс читает наши
982
+ `peerDependencies` для `@deepseek-ai/dsh*` не как версии пакетов, а как диапазон **версий
983
+ рантайма** (`evaluatePluginCompatibility`, с `includePrerelease`): неудовлетворённый диапазон —
984
+ это предупреждение «Plugin … is incompatible with dsh …» и запрос на исключение у пользователя.
985
+ Диапазон в манифесте — цепочка окон предрелизов, которую правка может незаметно сузить,
986
+ поэтому `npm run check:peers` (входит в `verify`) прогоняет это правило по всем заявленным
987
+ версиям. Сегодня проходят все восемь; доказано сужением диапазона до `>=0.2.1-0 <0.3.0-0` —
988
+ пять внятных провалов на старых версиях. Заодно в devDependencies добавлен `semver` (им же
989
+ пользуется харнесс).
990
+ - **Манифест проверяется по правилам харнесса.** Три правила вычитаны из его исходников, и
991
+ каждое — отказ, который иначе увидел бы пользователь: `parseDshClient` принимает `platform`
992
+ любой строкой, но потребители отбирают только `platform === 'web'`, поэтому опечатка `'Web'`
993
+ **молча выключила бы** клиентскую половину; объявленный `dsh.client` без экспорта `./client`
994
+ заставляет `client-modules` бросить ошибку (в том числе если файл по этому пути не существует);
995
+ `dsh.bundle.patch` обязан указывать на существующий файл. `npm run check:manifest` входит в
996
+ `verify` и доказан двумя подстановками: `'Web'` вместо `'web'` и удалённый экспорт — оба
997
+ пойманы с объяснением.
998
+ - **Разбор формулировок по всему пакету.** Прошёл по строкам с признаками машинного или
999
+ канцелярского перевода и поправил два места: «Копировать красивый JSON» → «Копировать
1000
+ **форматированный** JSON» (калька с `pretty`) и «нетекстовый **контент**» → «нетекстовое
1001
+ **содержимое**» (в пакете `содержимое` встречается 14 раз, `контент` — только в этих двух).
1002
+ Заодно закрыт класс, который нашёлся при разборе: обращение к пользователю. Внутри фразы
1003
+ русский интерфейс пишет «вы» со строчной, а «Вы» с заглавной — только в начале предложения,
1004
+ где это орфография; проверка смотрит строго на регистр и только на середину фразы.
1005
+ Любопытно, что первая версия правила была слишком широкой и завалила четыре правильные
1006
+ строки — «Вы вошли в DeepSeek» в начале фразы корректно, — так что правило уточнено, а не
1007
+ строки испорчены. Доказано подстановкой: «Ещё и Вы» → «Вы с заглавной в середине фразы».
1008
+ - **Отчёт о коротких строках, выросших в длине.** Русский длиннее английского по природе
1009
+ («Command failed» → «Команда завершилась с ошибкой»), поэтому отчёт показывает только
1010
+ крайние случаи — от 2,8× у коротких шаблонов (до 28 символов, без плейсхолдеров), где
1011
+ текст упирается в вёрстку кнопки, вкладки или значка. Таких три: «Effort» → «Уровень
1012
+ рассуждения», «SUBTOOL» → «ИНСТРУМЕНТ СУБАГЕНТА», «Job ID» → «ID фоновой задачи»; все три
1013
+ оставлены сознательно — это точные термины, а не многословие. Проверено и обратное:
1014
+ сравнение по первому попавшемуся шаблону давало 92 «нарушения», из которых почти все —
1015
+ артефакт чужого namespace, поэтому отчёт читает шаблон **своего** namespace.
1016
+ - **Механическая гигиена текста и краевые пробелы.** Первое — двойные пробелы, пробел перед
1017
+ знаком препинания, непарные «ёлочки» и скобки, неразрывный пробел внутри значения. Второе
1018
+ тоньше: значение, которое начинается или кончается пробелом, компонент склеивает с
1019
+ соседним, поэтому краевые пробелы сверяются с английским шаблоном **своего** namespace.
1020
+ Проверка сразу нашла два реальных дефекта в `schedule.manager`: у `delete.description` и
1021
+ `timing.conflict` английский шаблон кончается пробелом, а русский — нет, то есть
1022
+ приклеенный следом текст слипся бы со словом. Ещё две правки: разделитель тысяч
1023
+ `number.groupSeparator` стал неразрывным пробелом (числа больше не рвутся по строке), а
1024
+ «ссылка .tgz» в `installFailureNetworkHost` переписано в «прямая ссылка на .tgz».
1025
+ - **Проверка полноты покрывает все 54 namespace.** Привязка «namespace → словарь» строилась
1026
+ только по импорту словаря из того же каталога, поэтому `common` и `settings.locale` — чьи
1027
+ словари лежат в `../locales/` — оставались вне сверки: 52 из 54. Теперь разбирается и
1028
+ регистрирующая строка с идентификатором (`locale.register(NS, { zh, en })`), и имена
1029
+ импортированных словарей, так что каждый namespace пака сверяется со своим. Пробелов при
1030
+ этом не нашлось, но слепое пятно закрыто — и оно стало видимым: аудит печатает, сколько
1031
+ namespace сверено, а какие остались без привязки.
1032
+ - **Настоящие русские формы числа.** Контракт локали не несёт правил плюрализации:
1033
+ компонент выбирает `count === 1 ? '.one' : '.other'`, а русскому нужны три формы
1034
+ (1 вызов / 2 вызова / 5 вызовов). Пакет берёт выбор на себя — рядом с `.one`
1035
+ и `.other` в словарях появились `.few` и `.many` для 17 фраз (фоновые задачи,
1036
+ субагенты, шаги, вызовы инструментов, сообщения, сессии, участники запуска,
1037
+ напоминания, единицы работы в диалоге архивации), а обёртка над `translate`
1038
+ рантайма пересчитывает форму через `Intl.PluralRules('ru')` для любого вызова
1039
+ с числовым `count` или `n`. Форму решает число, а не ключ, который выбрал компонент:
1040
+ `t('…toolCalls.one', { count: 2 })` даёт «2 вызова инструментов», а `{ count: 5 }` —
1041
+ «5 вызовов инструментов». Ключи без новых форм сохраняют прежнее значение `.other`
1042
+ (нейтральную конструкцию), поэтому регрессия невозможна; выгрузка плагина снимает
1043
+ обёртку по счётчику владельцев. Проверка — `npm run check:plural`, четыре теста
1044
+ на настоящем рантайме локали, и она же добавлена в `verify` и оба workflow.
1045
+ - **Синхронизация с `dsh 0.2.1-alpha.3`**: покрытие — **2457 из 2457** ключей тега,
1046
+ 55 namespace. Харнесс добавил 28 ключей и один namespace — `secretMask` (пакет
1047
+ `ui-secret-mask`, показ и скрытие секрета). Наполнены шесть существующих namespace:
1048
+ `settings.models` (14 строк: вход в аккаунт, подсказки по ключам и переменным окружения,
1049
+ показ и скрытие секрета), `pluginManager` (8: причины перезапуска и предпросмотр
1050
+ возможности), `reference` (4: окно «не удалось открыть файл»), `workspace` (3:
1051
+ копирование ID сессии), `chat` (2: остановка задачи и прерванный ход), `conversation`
1052
+ (2: размер картинки в строке). Из пака удалены 7 строк, которых в теге больше нет
1053
+ (`message.accountStopped`, `settings.models.keyStored`, `keyEnvLocked`,
1054
+ `keyPlaceholderNative` и три ключа `settings.pluginInventory`), а `restartNotice`
1055
+ и `installDoneRestart` получили `{reason}`: тег теперь дописывает к ним причину
1056
+ перезапуска, и без плейсхолдера она не показывалась вовсе.
1057
+ - **Синхронизация с `dsh 0.2.1-alpha.2`**: покрытие — **2429 из 2429** ключей тега,
1058
+ 54 namespace. Добавлено 84 строки перевода: новый namespace `cotTranslation`
1059
+ (32 строки — перевод рассуждений и его настройки) и 52 строки в существующие —
1060
+ настройки шрифтов (20, `settings.theme`), менеджер плагинов (13),
1061
+ голосовой ввод (8), сворачивание хода работы в чате (4), аргументы команды и
1062
+ строка команды (2), миграция каталога и внешняя задача субагента (2),
1063
+ тосты ветвления сессии (2), ошибка последней попытки (1).
1064
+ - **Аудит получил третий гейт — паритет плейсхолдеров**: русское значение обязано
1065
+ нести ровно те `{name}`, что и английский шаблон ключа (для пакетов, которые везут
1066
+ только китайский словарь, шаблоном служит `zh`). Лишний плейсхолдер попадает на экран
1067
+ буквально, потерянный молча убирает подставленное значение. Проверка прогнана на живых
1068
+ дефектах: возвращённый `{duration}` в `message.turnProcess.took` валит аудит с кодом 1
1069
+ и печатает `ru: duration | шаблон: —`, а убранный `{duration}` из `chat.deepDivingFor` —
1070
+ `ru: — | шаблон: duration`. Сверено 2534 значения, шаблон недоступен у 35.
1071
+ - **`check:locale` в CI** — шесть проверок сосуществования и выгрузки словарей,
1072
+ пришедшие с PR #3.
1073
+ - **Собранный `lib/` в репозитории** — каталог плагинов генерирует команду без
1074
+ `@`-скоупа (`dsh plugin --profile web add Ragnoryok1/dsh-client-locale-ru`), и pnpm
1075
+ ставит её как архив GitHub. Проверено, что `prepare` этой задачи не решает: pnpm
1076
+ отказывается запускать сборочные скрипты git-зависимости
1077
+ (`ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED`, нужно вносить пакет в `allowBuilds`), то есть
1078
+ вместо тихой поломки выходит жёсткий отказ. Поэтому `lib/` теперь лежит в git, и
1079
+ установка из GitHub не требует сборки на стороне пользователя. `npm publish`
1080
+ по-прежнему пересобирает `lib/` через `prepublishOnly`.
1081
+ - **Одна английская фраза — один русский текст.** Новый гейт сверяет пакет с самим собой:
1082
+ если одну и ту же английскую фразу два namespace переводят по-разному, сборка падает.
1083
+ Сравниваются шаблоны от четырёх слов — короткая подпись законно зависит от контекста
1084
+ («Open» — это и «Открыть» в меню, и «Открыто» в статусе). Отчёт сразу нашёл шесть таких
1085
+ мест, и четыре оказались настоящими дефектами: `permission.access` обещал «уменьшать
1086
+ число шагов подтверждения», а `settings.permission` — «пропускать шаги», причём
1087
+ английское «subsequent tasks» в первом стало «текущей задачей» (оставлена точная
1088
+ формулировка, общая для обоих); «deployment» переводился как «сборка» в пяти namespace
1089
+ и как «развёртывание» в шестом — приведено к решению из `terminology.json` («деплой» →
1090
+ «развёртывание»), включая строки плагинного менеджера и `settings.models`; сообщения о
1091
+ пределе областей и о нехватке ширины разошлись формулировками при одинаковом английском
1092
+ (`command.budget`/`command.width` против `dock.splitPaneDisabled`/`dock.splitPaneNarrow`) —
1093
+ сведены к одной; у `terminal.expandRest` и `terminal.expandAria` не было форм числа, хотя у
1094
+ их двойников из плагинного менеджера они есть, — добавлены `.one/.few/.many`, и «… ещё
1095
+ строк: 3» стало «… ещё 3 строки». Заодно исправлен `createPluginMissing`: английский
1096
+ говорит «configuration», а в пакете стояло «сборка». Намеренное расхождение объявляется в
1097
+ `scripts/one-rendering.json` вместе с причиной, устаревшее исключение валит сборку. Гейт
1098
+ доказан тремя подстановками: расхождение без исключения → `exit 1` с обоими вариантами и
1099
+ их источниками; исключение, которое ничего не исключает → `exit 1`; расхождение с
1100
+ объявленным исключением → `exit 0` и «объявлено вручную: 1».
1101
+ - **Полнота namespace проверяется в обе стороны.** Аудит валил сборку только за namespace
1102
+ пака, которых нет в харнессе, а обратная дыра не проверялась: namespace, который харнесс
1103
+ регистрирует и наполняет ключами, а пак не регистрирует вовсе, оставляет свой экран
1104
+ английским, и покрытие по именам ключей этого не видит — те же ключи уже переведены под
1105
+ другим namespace. Харнесс регистрирует 63 namespace, пак вёз 55; из лишних восьми один
1106
+ оказался настоящим: `settings.sessionLog` — переключатель «Upload Session Log» на странице
1107
+ общих настроек (четыре строки, включая тосты «Preference saved» и «Could not save
1108
+ preference»). Namespace, найденные сканером регистраций без единого ключа (константы вроде
1109
+ `EMPTY_RESPONSE` или `normal`), в проверку не попадают: переводить в них нечего. Добавлен
1110
+ сам namespace (56-й), гейт доказан удалением: без него `exit 1` и строка
1111
+ `settings.sessionLog — ключей 4, регистрация: …UploadRow.tsx (PropsLocale)`.
1112
+ - **README-числа с разрядами и заявление о версии сборки.** Сверка заявленных чисел читала
1113
+ формы «, 2535 переводов», «, 2535 translated strings» и «、2535 条翻译», но не
1114
+ «2,489 strings» — и именно в этой форме в обоих английском и китайском README-лидах
1115
+ держалось число, разошедшееся на полсотни строк: шаблон требовал запятую перед числом, а не
1116
+ внутри него. Теперь разряды читаются, и на подстановке старый текст поймал себя сам
1117
+ («заявлено 2489 строк, а в пакете 2542»). Рядом появилась сверка фразы «built against dsh
1118
+ `X`»: она сравнивается с тегом `HARNESS_TAG` из `verify.yml` — тем самым, которым пакет и
1119
+ проверяется, — потому что проза и workflow расходятся молча, а читатель сравнивает паки по
1120
+ этой фразе, а не по workflow. Оба README-лида обещали `0.2.1-alpha.2` при закреплённом
1121
+ `dsh-v0.2.1-alpha.3`; исправлено, и проверка доказана подстановкой обеих частей.
1122
+ - **Собранный `lib/` сверяется с коммитом.** Установка из GitHub исполняет закоммиченный
1123
+ `lib/client.js` (pnpm отказывается запускать сборочный скрипт git-зависимости), поэтому
1124
+ устаревший бандл попадает к пользователю, а не в аудит — аудит читает исходники, а `verify`
1125
+ просто пересобирал поверх. Теперь `npm run check:built` пересобирает `lib/` и сравнивает
1126
+ каждый отслеживаемый файл с `HEAD` (переводы строк нормализуются, поэтому CRLF-чекаут и
1127
+ LF-чекаут сходятся), а незакоммиченный файл сборки тоже считается расхождением. Сборка
1128
+ детерминирована — два прогона дают одни и те же байты, это и делает сравнение осмысленным.
1129
+ Проверка доказана подстановкой: правка одного значения в `dicts.ts` без коммита даёт
1130
+ `exit 1` и «lib/client.js — расходится с HEAD», после откатa — снова `exit 0`.
1131
+ - **Заявленный размер пакета меряется, а не запоминается.** «Публикуемый пакет занимает
1132
+ 354 КБ» было написано раньше и с тех пор разошлось с реальностью на 27 КБ: словари выросли,
1133
+ а число — нет. `npm run check:size` собирает архив (`npm pack --dry-run --json`) и сверяет
1134
+ заявленные килобайты с настоящими во всех трёх README, с допуском в один килобайт: gzip на
1135
+ разных сборках Node сжимает чуть иначе, а фраза округлена до целых килобайт. На подстановке
1136
+ проверка поймала все три README сразу и назвала измеренное значение (381 КБ).
1137
+ - **Описание в `package.json` — четвёртое место тех же чисел.** Сверка заявлений читала только
1138
+ README, а `description` в манифесте — это то, что npm показывает в поиске и на странице
1139
+ пакета; там дольше всех жило «55 namespaces». Теперь манифест читается тем же кодом, и
1140
+ фраза «verified against dsh X» сверяется с закреплённым тегом наравне с «built against».
1141
+ Доказано подстановкой: «55 namespaces» и `0.2.1-alpha.2` в описании — `exit 1` с двумя
1142
+ отдельными сообщениями.
1143
+ - **CI прогоняет те же гейты, что `npm run verify`.** `verify.yml` вызывал пять проверок
1144
+ вручную, а `check:readmes`, `check:manifest`, `check:peers`, `check:upstream` не запускал
1145
+ вовсе — то есть заявления README в CI не проверялись никогда, и зелёный прогон об этом
1146
+ молчал. Список шагов выровнен по `verify`, добавлены новые `check:built` и `check:size`;
1147
+ `publish.yml` получил те же шаги (включая сверку собранного бандла перед публикацией).
1148
+ - **Карта исходников была платформенной — и первый же прогон CI это поймал.** Новый гейт
1149
+ `check:built` упал на Linux: `lib/client.js.map` расходился с коммитом. Причина не в бандле
1150
+ (`lib/client.js` и все `.d.ts` совпали байт в байт) — tsdown вкладывает в карту
1151
+ `sourcesContent` ровно так, как прочитал файлы, то есть с `\r\n` на Windows и с `\n` на
1152
+ Linux. Нормализация переводов строк в самой проверке этого не лечит: в JSON это Escaped-
1153
+ последовательность из двух символов, а не настоящий перевод строки. Сборка теперь кончает
1154
+ шагом `scripts/normalize-map.mjs`, который приводит `sourcesContent` к LF — той форме, в
1155
+ которой git и хранит исходники; карта снова детерминирована. Гипотеза проверена не на глаз:
1156
+ копия репозитория с LF-исходниками собирается в те же шесть файлов, что и коммит (до правки
1157
+ расходилась ровно карта, после — ни один).
1158
+ - **Плейсхолдеры наших собственных форм числа теперь сверяются.** Сверка сравнивала значение
1159
+ со шаблоном харнесса, а у форм, которых в харнессе нет, шаблона нет по определению —
1160
+ то есть 52 значения (все добавленные `.few`/`.many` и `.one` у непарованных ключей) не
1161
+ проверялись вовсе, и потерянный `{count}` в такой форме никто бы не увидел. Теперь форма
1162
+ сверяется с шаблоном соседней формы того же слова (`.one`/`.other`) или самого ключа;
1163
+ проверено, что шаблона не нашлось вовсе у 0 значений. Доказано подстановкой: убранный
1164
+ `{count}` из `count.live.few` → `exit 1` с «ru: — | соседняя форма: count».
1165
+ - **Отчёт «нет шаблона» печатал только счётчик.** Число «шаблон недоступен у 49» ничего не
1166
+ говорило о том, что это за ключи, — а именно список показывает, дыра это парсера или
1167
+ наша собственная форма. Теперь ключи перечислены; после правки выше список пуст, и это
1168
+ утверждение проверяемо, а не подразумеваемо.
1169
+ - **«Cron-выражение» чуть не стало «cron-выражение» — стилевой гейт это остановил.** Показалось,
1170
+ что в одном namespace одно и то же слово написано по-разному: подпись поля с заглавной, а
1171
+ подсказка под ним со строчной («Введите cron-выражение из пяти полей»). Правка была неверной:
1172
+ метка поля — не предложение, и в русском она начинается с заглавной, ровно как английский
1173
+ шаблон («Cron expression»), а строчная нужна внутри фразы. Проверка регистра первой буквы
1174
+ сказала это сама (`регистр: у нас "cron-выражение" | шаблон "Cron expression"`), значение
1175
+ возвращено. Тот же урок, что с «Вы» в начале фразы: сначала проверка, потом правка.
1176
+ - **Проверено, что чинить нечего.** Прогнал по пакету два класса, обещанных в прошлом круге:
1177
+ смешанное написание ё и дефис вместо тире внутри значений. Ё — 64 слова, у всех одна форма;
1178
+ единственное «совпадение» оказалось разными словами («все» и «всё»), то есть проверять
1179
+ нечего. Дефис вместо тире — 0 значений, тире без пробела — 0. Гейт на это не заводил: он
1180
+ ловил бы «все/всё» как ложное срабатывание, а дефектов нет.
1181
+ - **Решения `keep-english.json` больше не гниют.** Файл разрешает оставить английский текст,
1182
+ и лишняя запись в нём молча расширяет разрешение — тот же риск, что у исключений
1183
+ `one-rendering.json`. Теперь каждая запись обязана что-то разрешать: значение ключа
1184
+ совпадает с шаблоном харнесса. Запись, у которой ключа в пакете уже нет, и запись, чьё
1185
+ значение перевели, — обе валят сборку, с разными сообщениями, чтобы правка была очевидной.
1186
+ Проверка сразу нашла `settings.models · deepSeekBaseUrl`: английский шаблон обзавёлся
1187
+ пояснением «(Default to use DEEPSEEK_BASE_URL)», а наше значение осталось голым адресом —
1188
+ и это правильно, потому что ключ служит подсказкой внутри поля ввода, где пояснение было бы
1189
+ мусором. Запись убрана, значение оставлено. Доказано двумя подстановками: несуществующий
1190
+ ключ → «такого ключа в пакете нет», переведённое значение → «значение уже не совпадает с
1191
+ шаблоном, запись ничего не разрешает».
1192
+ - **Видимая зона, до которой сверка остатков не достаёт.** Сверка остатков читает только
1193
+ блоки `en`, а у части словарей харнесса этот блок не опознаётся — и такие значения не
1194
+ проверялись вовсе. Попытка «просто расширить сверку на все шаблоны» дала 50 находок, и все
1195
+ пятьдесят оказались названиями приложений и форматными строками: расширять гейт значило бы
1196
+ потребовать полсотни новых записей политики ради того, что английским и должно быть.
1197
+ Поэтому зона не расширена, а **напечатана**: справочный список из 60 значений — названия
1198
+ приложений (`VS Code`, `iTerm2`), форматные строки (`{from}–{to}`, `{percent}%`), метка
1199
+ `en`, подписи `Cron {expression}`. Список проверяем глазами и меняется при правках, поэтому
1200
+ переведённая фраза в этой зоне не спрячется незаметно; в README это записано прямо, а не
1201
+ выдано за полное покрытие.
1202
+ - **Зона закрыта как класс: шаблоны в политике и форматные строки по правилу.** Справочный
1203
+ список из прошлого круга был признанием, а не решением, поэтому следующим шагом зона стала
1204
+ гейтом. Три изменения. Во-первых, запись политики может называть закрытый список:
1205
+ `chat · app.*` разрешает тридцать названий приложений одной строкой — словарь имён
1206
+ приложений и есть список имён; шаблон обязан называть namespace, иначе одна звёздочка
1207
+ разрешила бы всё (проверка на загрузке политики, `exit 2`). Во-вторых, значение без слов
1208
+ вне плейсхолдеров (`{from}–{to}`, `{percent}%`, `, `) не требует решения вовсе: проверка
1209
+ снимает плейсхолдеры и ищет букву — форматная строка не бывает «непереведённой прозой»,
1210
+ потому что прозы в ней нет; восемь записей после этого стали лишними и удалены. В-третьих,
1211
+ сверка остатков читает все шаблоны ключа, а не только блок `en` — та самая зона исчезла:
1212
+ сверено 2802 значения (было 2579), 68 покрыты решениями, 38 записей, из них одна шаблон.
1213
+ - **Пустое значение больше не валит сборку там, где пуст и шаблон.** Расширение сверки на все
1214
+ шаблоны сразу поймало `chat · message.stepProcess.sharedPrefix`: у нас пустая строка, а
1215
+ «шаблон» — китайское 「已」. Английский шаблон тоже пуст, просто раньше сверка его не видела.
1216
+ Правило стало точнее того, что было: пустое значение — ошибка только там, где **ни один**
1217
+ шаблон не пуст, иначе русский справедливо опускает префикс, который английский опускает
1218
+ тоже. Доказано на живом ключе, а не на подстановке: до правки — `exit 1`, после — `пустые 0`.
1219
+ - **Шаблоны политики тоже проверяются на гниль.** Шаблон, под который не подходит ни одно
1220
+ значение, требующее решения, теперь валит сборку — иначе одна опечатка в `chat · app.*`
1221
+ тихо разрешала бы пустоту. Доказано подстановками: переименованный шаблон
1222
+ (`chat · app.qqq*`) → «под шаблон не подходит ни одно значение, которое нужно разрешить»;
1223
+ шаблон без namespace → `exit 2`; сломанный шаблон (`chat · app.nope` без звёздочки) →
1224
+ «такого ключа в пакете нет» и 31 неразрешённая копия.
1225
+ - **Каждой паре форм харнесса нужны русские `.few`/`.many`.** Обёртка подставляет форму
1226
+ только если словарь её несёт, иначе молча возвращается к нейтральному `.other` — то есть
1227
+ отсутствие формы не падает и не мигает, просто «2 дня» превращается в «2 д». Новый гейт
1228
+ сверяет это **по namespace**: обёртка ищет форму в том namespace, который спросил, значит
1229
+ пара, забытая на одном экране при наличии на другом, иначе невидима. Нашлись четыре
1230
+ единицы времени в `schedule.catalog` (восемь форм) и `trigger` в менеджере задач (две).
1231
+ Заодно исправлены сами единицы: они были сокращениями («д», «ч», «мин», «с»), хотя
1232
+ подставляются в прозу — «через 2 д», «Раз в 30 мин». Теперь полные формы с правильным
1233
+ падежом: день/дня/дней, час/часа/часов, минуту/минуты/минут, секунду/секунды/секунд —
1234
+ «через 2 дня», «через 5 часов», «через 1 минуту». Это же сняло согласование в шапке:
1235
+ «Каждые {value} {unit}» давало «Каждые 1 день», стало «Раз в {value} {unit}» — верно для
1236
+ любого числа и любого из четырёх слов.
1237
+ - **Тест форм числа перестал фиксировать дефект.** Кейс проверял, что `unit.day` **не имеет**
1238
+ `.few`/`.many` и возвращает «д»: он документировал пробел как норму. Теперь он проверяет
1239
+ обратное — «1 день», «2 дня», «5 дней», «3 часа» и «1,5 минуты» для дробного счёта, где
1240
+ CLDR не даёт формы и срабатывает именно `.other`.
1241
+ - **Значения проверяются на типографику отдельно от DOM.** Проход типографики видит только
1242
+ текстовые узлы, а значение из словаря попадает ещё и в атрибуты — `title`, `aria-label`,
1243
+ `placeholder`, — до которых он не достаёт. Значит правило «исправлю при отрисовке» для них
1244
+ не работало вовсе, и одна и та же строка выглядела бы по-разному в зависимости от места.
1245
+ Новый гейт требует, чтобы значения были уже правильными: без прямых кавычек, без дефиса
1246
+ вместо тире, без тире без пробела и без трёх точек. Сейчас дефектов 0 — это защита от
1247
+ регрессии, а не находка.
1248
+ - **Совместимость с чужим русским пакетом — проверено, а не предположено.** В
1249
+ `@deepseek-ai/dsh-client-locale` вызов `addLanguage` бросает `locale "ru" is already
1250
+ registered`, если `ru` уже занят, — с русским пакетом конкурента это теперь реальный
1251
+ сценарий. Наш `apply` ловит **только** это сообщение и только для занятой записи: язык и
1252
+ словари первого регистранта остаются, остальные наши namespace регистрируются, а любая
1253
+ другая ошибка по-прежнему валит активацию. Поведение закреплено тестами `check:locale`
1254
+ («другой пакет первым», «занятый namespace», «наша активация второй», «чужая ошибка не
1255
+ проглатывается»); отдельно сверено с кодом конкурента: он глушит **все** ошибки регистрации
1256
+ пустым `catch {}`, то есть при обратном порядке установки теряет и настоящие сбои.
1257
+ - **Даты и часовые пояса теперь по-русски — и это закреплено тестом.** Тот же тег `time.locale`
1258
+ печатает отметки времени: с `en` русский читатель видел «Oct 5, 2026, 3:51 PM». `check:composition`
1259
+ получил шестой случай — он берёт наборы опций из `formatScheduleAbsolute` и
1260
+ `formatScheduleNextRun`, форматирует фиксированный момент и требует кириллический месяц
1261
+ («5 окт. 2026 г., 15:51») и отсутствие латинских слов, а от подписи пояса — «Москва» при
1262
+ сохранённом ASCII-префиксе `UTC`. Строки сравниваются по признакам, а не по точному
1263
+ равенству: ICU в разных сборках Node расставляет пробелы и сокращения по-своему, и тест не
1264
+ должен падать от чужой версии `Intl`. Доказано подстановкой: `time.locale` = `en` →
1265
+ `actual: 'Oct 5, 2026, 15:51'` против `/окт/`, `exit 1`.
1266
+ - **Описание cron переведено заново: счёт — через формы числа, остальное — без книжных
1267
+ оборотов.** В `cron.time.*` стояли сокращения («Каждые {step} мин», «Каждые {count} ч») и
1268
+ кальки («Каждый час в минуту {minutes}», «минуты {minutes} в часы {hours}», «каждую минуту в
1269
+ часы {hours}»). Счётные ключи получили `.few`/`.many`: «Каждые 3 минуты» / «Каждые 5 минут»,
1270
+ «Каждые 2 часа» / «Каждые 6 часов» — обёртка подставляет их сама, потому что `{step}` и
1271
+ `{count}` — первые числовые параметры вызова. Остальные переписаны в один читаемый вид с
1272
+ метками: «Каждый час, минуты: 5, 30», «каждую минуту, часы: 09–17», «каждые 5 минут, часы:
1273
+ 09–17», «минуты: 5, часы: 09–17». `check:composition` получил пятый случай, который
1274
+ складывает фразу расписания целиком — «Пн–Пт каждые 5 минут» — и проверяет формы через
1275
+ настоящий `translate`. Доказано подстановкой: возвращённое «Каждые {count} ч» → `actual:
1276
+ 'Каждые 2 ч'` против ожидаемого `'Каждые 2 часа'`, `exit 1`.
1277
+ - **«Каждый день в January» — `time.locale` был не текстом, а тегом `Intl`.** Харнесс строит
1278
+ названия месяцев и подписи часовых поясов через `Intl.DateTimeFormat(t('time.locale'))` и
1279
+ передаёт тот же тег в разбор cron. Английский словарь харнесса держит там `en`, китайский —
1280
+ `zh-CN`, а наш пакет унаследовал `en`: правило с ограничением по месяцу показывало
1281
+ «Каждый день в January», а подпись пояса — «Moscow Standard Time» в русском интерфейсе.
1282
+ Теперь тег `ru` (оба namespace), а `cron.months` переписан под именительный падеж, который
1283
+ даёт `Intl`: « в {months}» → « ({months})», то есть «Каждый день (январь, март)» вместо
1284
+ невозможного «в январь». Записи политики `keep-english.json` про `time.locale`
1285
+ («код языка, который подставляет браузер») после этого перестали что-либо разрешать —
1286
+ гейт устаревших решений указал на обе, и они удалены: 36 записей вместо 38.
1287
+ - **`check:composition` получил четвёртый случай** — правило с ограничением по месяцу: тег
1288
+ обязан быть `ru`, месяцы приходят из `Intl` по-русски («январь, март»), фраза целиком —
1289
+ «Каждый день (январь, март)», и в ней не остаётся ни одного латинского слова. Доказано
1290
+ подстановкой: `time.locale` = `en` → `actual: 'en'` против ожидаемого `'ru'`, `exit 1`. Заодно это первая проверка, которая
1291
+ читает **тег**, а не текст: значение, управляющее форматированием, проверяется тем, что оно
1292
+ производит.
1293
+ - **«По Пн, Ср в 09:00» — фраза, которую не видела ни одна построчная проверка.** Аудит
1294
+ сверяет значение с его собственным шаблоном, поэтому фраза, верная в одиночку, но сломанная
1295
+ подстановкой, остаётся невидимой: `frequency.weekly` был переведён как
1296
+ «По {weekdays} в {time}», а `{weekdays}` приходит как «Пн, Ср» — «По» требует дательного
1297
+ множественного («по понедельникам»), которого у сокращений нет. Теперь «{weekdays} в {time}
1298
+ ({timeZone})» → «Пн, Ср в 09:00 (Europe/Moscow)». Заодно появился `check:composition`:
1299
+ три проверки складывают реальные значения так, как это делает харнесс, и читают результат
1300
+ целиком — еженедельное правило, ежедневное с зоной и интервал с формами числа
1301
+ («Раз в 2 дня», «Раз в 5 дней», «Раз в 1 минуту»). Входит в `verify` и в CI. Доказано
1302
+ подстановкой: возвращённое «По {weekdays}» даёт ровно `actual: 'По Пн, Ср в 09:00 …'`
1303
+ против ожидаемого, `exit 1`.
1304
+ - **`check:readmes` не зависел от перевода строк в рабочей копии.** Скрипт резал файл по
1305
+ `\n` и искал заголовки выражением `^(#{1,6})\s+(.*)$`: в строке, заканчивающейся `\r\n`,
1306
+ точка не доходит до `\r`, и заголовок не находился. На Linux (LF) всё проходило, а после
1307
+ `git checkout` на Windows с `autocrlf` тот же файл давал «заголовков 0» — гейт падал на
1308
+ исправном документе. Переводы строк нормализуются перед разбором: проверка описывает
1309
+ документ, а не машину, на которой его прочли. Доказано, что различие при этом всё ещё
1310
+ ловится: лишний заголовок в `README.en.md` → «заголовков 16, а в README.md 15», `exit 1`.
1311
+ - **Копия английского с другим регистром тоже ловится.** Сверка остатков требовала точного
1312
+ совпадения с шаблоном, поэтому `Cancel` при шаблоне `cancel` проходила: на экране это
1313
+ непереведённый английский текст, а все проверки зелёные. Теперь значение, отличающееся от
1314
+ шаблона только регистром или краевыми пробелами, — такая же копия и требует решения;
1315
+ нормализуется ровно это, поэтому настоящее изменение слова по-прежнему идёт через точную
1316
+ сверку и политику. Доказано подстановкой: `baseUrl` = `BASE URL` → «совпадает с шаблоном
1317
+ без учёта регистра», `exit 1`; после откатa — 0.
1318
+ - **Две проверки приведены в согласие со своими обещаниями.** Сверка плейсхолдеров сравнивает
1319
+ их **по порядку** (перестановка `{a}`/`{b}` — это подстановка числа не в тот слот), а
1320
+ комментарий обещал множество; обещание исправлено, а не поведение. У `check:size` допуск
1321
+ был 2 КБ при тексте «1 КБ» в README и в собственном комментарии — теперь ровно 1 КБ,
1322
+ проверено подстановкой: заявленные 392 КБ при пакете 394 КБ валят сборку, хотя раньше
1323
+ проходили.
1324
+ - **Проверено обратное направление «одна фраза — один перевод».** Гейт ловит одну английскую
1325
+ фразу, переведённую по-разному; теперь печатается и зеркало — одинаковый русский ответ на
1326
+ **разные** английские фразы от четырёх слов. Это след копипасты (подсказка, приехавшая от
1327
+ соседнего ключа) или два разных факта, слитых в одну фразу. Гейтом это быть не может:
1328
+ русский законно сливает слова, которые английский различает («Save» и «Keep» — оба
1329
+ «сохранить»), поэтому печатаются обе стороны, а решение остаётся за человеком. Сейчас
1330
+ находок 0; проверено подстановкой: копия длинного значения `keyHelp` в `keyPlaceholderSaved`
1331
+ дала ровно одну строку с обеими английскими фразами.
1332
+ - **Свои формы числа тоже под гейтом.** Харнесс передаёт счёт ключам, которые никогда не
1333
+ паровал (`terminal.expand`, `terminal.expandAria`, `terminal.expandRest` и их двойники в
1334
+ менеджере плагинов) — там формы держатся только на пакете, и потеря `.few` не проявилась бы
1335
+ ни в чём: обёртка молча вернулась бы к базовому ключу без склонения. Проверка распространена
1336
+ на эти базы: у каждой должны быть `.few` и `.many`. Доказано подстановкой: удалённый
1337
+ `terminal.expand.few` → `job · terminal.expand.few`, `exit 1`; после откатa — 0.
1338
+ - **Прогон в CI раз в неделю.** Перевод гниёт и без наших правок: харнесс уезжает вперёд,
1339
+ локально всё зелено, а расхождение всплывает только при сборке у пользователя. В
1340
+ `verify.yml` добавлено расписание (понедельник, 06:00 UTC): тот же полный `verify` против
1341
+ закреплённого тега, без пуша и без человека.
1342
+ - **Метаданные для поиска.** В `keywords` добавлены `dsh-plugin` (по нему ищут в маркетплейсе
1343
+ плагинов), `locale` и `typography` — сам пакет и его правила теперь находятся по тем
1344
+ запросам, которыми их ищут.
1345
+
1346
+ ### Исправлено
1347
+
1348
+ - **Аудит перестал пропускать целые словари**: ключи без кавычек (`panel: '插件'`
1349
+ в менеджере плагинов) и словари с аннотацией типа
1350
+ (`export const zh: Record<Key, string> = {` в голосовом вводе, субагенте и
1351
+ траектории) не читались, поэтому проверка плейсхолдеров смотрела 1621 значение
1352
+ вместо 2462. Сверка идёт по всем шаблонам ключа, а не по первому найденному:
1353
+ разные ключи `invalid` в трёх namespace больше не дают ложного расхождения.
1354
+ - **Повторная регистрация `ru` больше не срывает активацию** (PR #3, автор @knopki):
1355
+ когда другой языковой пакет (`@linxin666/dsh-i18n` — первый известный случай)
1356
+ регистрирует `ru` или занимает namespace раньше, пакет пропускает ровно эту ошибку
1357
+ дублирования, продолжает регистрировать остальные словари и не забирает себе чужие
1358
+ ресурсы. До этого активация обрывалась на `locale "ru" is already registered`,
1359
+ и русские словари ядра не подключались вовсе.
1360
+ - **`message.turnProcess.took` больше не показывает плейсхолдер** (PR #2, автор @knopki):
1361
+ харнесс рендерит этот ключ без параметров и сам добавляет длительность, поэтому
1362
+ `Заняло {duration}` попадало на экран как «Заняло {duration}7мин 26с». Теперь это
1363
+ просто префикс `Заняло `. Тем же изменением удалён мёртвый ключ
1364
+ `message.turnProcess.deepDivingFor` — живой ключ называется `chat.deepDivingFor`.
1365
+ - **Счётчик переводов в README приведён к факту**: 2417 вместо 2418 — удаление мёртвого
1366
+ ключа уменьшило набор на одну строку, а `check:readmes` сверяет структуру, а не числа.
1367
+
1368
+ ### Изменено
1369
+
1370
+ - **Пакет стал в семь раз легче: 2,6 МБ → 354 КБ.** Баннер для README переведён из PNG
1371
+ в JPEG (1612 → 152 КБ) без потери качества; иконка и карта исходников в npm больше не
1372
+ входят. Иконка остаётся в репозитории — каталог плагинов ссылается на неё абсолютным
1373
+ адресом, — а карта исходников остаётся в git для установки из репозитория.
1374
+ - **Декларации типов генерируются сборкой.** В `lib/` лежали `index.d.ts` и `client.d.ts`
1375
+ от 2 сентября, причём `lib/typography.d.ts`, на который ссылался `exports`, не
1376
+ существовал вовсе. Теперь `tsdown` emits `index.d.ts` и `typography.d.ts` из текущего
1377
+ исходника, а `./client` больше не обещает файл типов: это модуль для клиентского
1378
+ загрузчика с собственным баннером, а не TypeScript-API.
1379
+ - **`@deepseek-ai/dsh-client-locale` в devDependencies закреплён точно на версии тега** —
1380
+ `0.2.1-alpha.3`, вместе с `@deepseek-ai/cordis@4.0.5-alpha.2`. Плавающий диапазон
1381
+ резолвился в чужую комбинацию и ронял `npm install` в CI с `ERESOLVE`, то есть сборка была
1382
+ красной не из-за кода. Закреплённая пара даёт то, ради чего она нужна: `check:locale`
1383
+ и `check:plural` выполняются на том же рантайме локали, который получают пользователи, а
1384
+ не на более старом `0.2.0-rc.2`. Публикуемый peer-диапазон `@deepseek-ai/cordis` расширен
1385
+ до `^4.0.2 || >=4.0.5-0 <4.0.6-0`: иначе предрелиз `4.0.5-alpha.2` из тега не проходит
1386
+ проверку peer-зависимостей.
1387
+
1388
+ ## [0.2.0] — 2026-10-03
1389
+
1390
+ ### Добавлено
1391
+
1392
+ - **Синхронизация с `dsh 0.2.1-alpha.1`**: 76 новых ключей — пять новых namespace
1393
+ (`settings.agentPreset`, `settings.account`, `inspectorPanel`, `claude-code-mods`,
1394
+ `session-inspector`) и три расширенных (`pluginManager` +8, `workspace` +2,
1395
+ `schedule.manager` +1). Покрытие — **2364 из 2364** ключей новой базы.
1396
+ - Переведены панель инспектора сессий (52 строки), установка плагинов из npm/Git/каталога,
1397
+ режим создания плагина агентом и напоминание о том, что сессия субагента не получает
1398
+ доставленные уведомления.
1399
+
1400
+ ### Исправлено
1401
+
1402
+ - **Три имени namespace сверены с кодом харнесса**: предположения по документации
1403
+ (`agent-preset`, `account`, `inspector`) оказались неверными — настоящие имена
1404
+ `settings.agentPreset`, `settings.account` и `inspectorPanel`. Переводы под
1405
+ неверными именами не показывались бы никогда.
1406
+ - **Аудит получил новый гейт**: namespace, объявленный дважды, перекрывает сам себя в
1407
+ объектном литерале и молча роняет все переводы внутри, при этом покрытие по ключам
1408
+ остаётся полным. Проверено: с дублем аудит падает с кодом 1.
1409
+
1410
+ ### Изменено
1411
+
1412
+ - Версия пакета поднята до **0.2.0** — пакет следует за веткой харнесса 0.2.x.
1413
+ - Аудит в CI переведён на тег `dsh-v0.2.1-alpha.1`.
1414
+
1415
+ ## [0.1.9] — 2026-10-02
1416
+
1417
+ ### Добавлено
1418
+
1419
+ - **Синхронизация с `dsh 0.2.0-rc.2`**: 37 новых ключей в шести namespace — `chat`
1420
+ (единицы длительности и строка «Углублённый анализ»), `conversation` (вопросы,
1421
+ ожидающие ответа), `model` (пустой поиск), `pluginManager` (подсказки установки,
1422
+ несовместимость версий, порядок обновления), `question` (асинхронные вопросы:
1423
+ ожидание, ответы, пропуск) и `workspace` (сессии без названия).
1424
+ Покрытие — **2287 из 2287** ключей новой базы.
1425
+ - **Расширен диапазон совместимости** до `>=0.1.0-rc.2 <0.3.0`: контракт локали
1426
+ (`addLanguage` и `register`) в 0.2.0 не изменился, проверено по дереву тега.
1427
+
1428
+ ### Изменено
1429
+
1430
+ - Аудит в CI переведён на тег `dsh-v0.2.0-rc.2`; проверка против 0.1.7-rc.2
1431
+ по-прежнему проходит (2264 из 2264), поэтому пакет работает на обеих ветках.
1432
+
1433
+ ## [0.1.8] — 2026-09-27
1434
+
1435
+ ### Добавлено
1436
+
1437
+ - **Русская экранная типографика** (`src/client/typography.ts`): «ёлочки» вместо прямых
1438
+ кавычек, длинное тире между словами, многоточие, неразрывный пробел между числом и
1439
+ следующим словом или единицей измерения. Правила осторожные: код, поля ввода,
1440
+ редактируемый текст и `[data-typography="off"]` не трогаются, повторный проход ничего не
1441
+ меняет. Работает как `ctx.effect`, поэтому выгружается вместе с плагином.
1442
+ - **Проверки в CI** (`.github/workflows/verify.yml`): сборка, 12 правил типографики
1443
+ (`scripts/check-typography.mjs`) и аудит локали (`scripts/audit.mjs`) против
1444
+ **закреплённого тега** харнеса. Новые npm-скрипты: `check:typography`, `audit`, `verify`.
1445
+ - Раздел README **«Область покрытия»** с измерением: из 17 самых скачиваемых плагинов
1446
+ экосистемы 11 не поставляют собственных строк, поэтому их русский интерфейс обеспечивают
1447
+ словари харнеса; словари сторонних плагинов в область пакета не входят.
1448
+
1449
+ ### Исправлено
1450
+
1451
+ - **Число переводов в документации**: 2598 → **2304**. Прежняя цифра считала повторяющиеся
1452
+ имена ключей в разных namespace дважды.
1453
+
1454
+ ## [0.1.7] — 2026-09-27
1455
+
1456
+ ### Исправлено
1457
+
1458
+ - **Мёртвый namespace `schedule.catalog`.** В 0.1.6 было ошибочно записано, что этот
1459
+ namespace упразднён и заменён на `schedule.manager`. На деле в харнесе существуют оба:
1460
+ `schedule.catalog` — каталог напоминаний в шапке сессии вместе с частотами и единицами
1461
+ времени, `schedule.manager` — менеджер задач автоматизации. Из-за неверного вывода
1462
+ около 30 строк перевода (`trigger.*`, `list.*`, `delete.*`, `frequency.*`, `cron.*`,
1463
+ `unit.*`, `relative.*`, `mark.aria`, `hover.more`) регистрировались под именем, которое
1464
+ никто не читает, и никогда не показывались в интерфейсе.
1465
+ - **Числительные, неверные на числах 2–4.** Русский требует трёх форм для `n ≥ 2`, а
1466
+ компоненты харнесса выбирают форму как `count === 1 ? 'ключ.one' : 'ключ.other'`.
1467
+ Переписаны 17 пар ключей (`.one` — естественная форма для `n = 1`, `.other` —
1468
+ конструкция, корректная при любом `n ≥ 2`) и 8 строк с `{count}` перед склоняемым
1469
+ словом. Пример: «2 субагентов» → «Субагентов: 2», «каждые 2 часов» → «каждые 2 ч»,
1470
+ «ещё 3 строк» → «ещё 3 стр.».
1471
+ - Единицы измерения в частоте повтора: `unit.hour.other` «часов» → «ч» и аналогично
1472
+ для минут, секунд и дней; `cron.time.everyMinutes` «минут» → «мин».
1473
+
1474
+ ### Добавлено
1475
+
1476
+ - **Namespace `schedule.manager` (188 ключей)** — менеджер задач автоматизации полностью
1477
+ на русском: панель и каталог задач, поиск, фильтр состояний, сведения о задаче и её
1478
+ вкладки, журнал запусков с правилами хранения, редактор расписания (дата, время, час,
1479
+ минута, секунда, часовой пояс, интервал повтора), правила повтора (разово, каждые
1480
+ N минут/часов/секунд, ежедневно, понедельник — пятница, еженедельно, свой cron),
1481
+ cron-форма с выбором дат и минуты, удаление с подтверждением, уведомления, карточки.
1482
+ 55 ключей приходят из общего frequency-словаря, поэтому их текст совпадает с каталогом.
1483
+ - 7 ключей: `blocked.composer`, `defaultWorkspace.title`, `schedule.active`,
1484
+ `settings.transcript.expanded`, `status.scheduled`, `status.overdue`, а также namespace
1485
+ `shortcuts.layout` с подписью команды показа левой панели.
1486
+
1487
+ ### Инструменты
1488
+
1489
+ - Аудит **имён** namespace (`audit-namespaces.mjs`): имена извлекаются из всех способов
1490
+ объявления (`const NS`/`SETTINGS_NS`/`namespace`, `locale.register`, `locale.bind`,
1491
+ `PropsLocale<…>`) и сравниваются множествами. Прежняя проверка покрытия сверяла ключи
1492
+ внутри namespace, но не сверяла имена, из-за чего целый namespace мог тихо не работать.
1493
+
1494
+ ### Состояние
1495
+
1496
+ 52 namespace, 2304 перевода, ~2700 строк. Проверки: 0 отсутствующих ключей против
1497
+ `dsh 0.1.7-rc.2`, 0 мёртвых namespace, 45/45 пар ключей с формами числа.
1498
+
1499
+ ## [0.1.6] — 2026-09-25
1500
+
1501
+ Догнал `dsh 0.1.7-rc.2`: 252 ключа добавлено, 18 обновлено, 26 удалено.
1502
+ Появились разделы `shortcuts` (редактор горячих клавиш), `schedule.manager`,
1503
+ `settings.account`; исправлена единица счёта в инвентаре плагинов («187 плагины» →
1504
+ «187 плагинов»); добавлены npm keywords, homepage, badges и снимки интерфейса; исправлен
1505
+ peer-диапазон `@deepseek-ai/dsh-client-locale` на `>=0.1.0-rc.2 <0.2.0` (старый `^0.1.0`
1506
+ не разрешался, так как пакет публикуется только под prerelease-версиями).
1507
+
1508
+ ## [0.1.5] — 2026-09-24
1509
+
1510
+ Догнал `dsh 0.1.7-rc.1`: 49 ключей добавлено, 13 обновлено, 16 удалено, namespace
1511
+ `directory-browser` убран. Переведены подписи шагов работы инструментов, полноэкранный
1512
+ просмотр изображений, режимы показа ходов, установка плагинов из GitHub, сообщения команды
1513
+ `Cordis`. Уточнены формулировки там, где upstream заменил «teammate/agent» на «subagent»,
1514
+ а «Beta» — на «Experimental».
1515
+
1516
+ ## [0.1.4] — 2026-09-23
1517
+
1518
+ Догнал `dsh 0.1.7-alpha.2`: 905 ключей добавлено, 29 изменены, 126 удалены. Появились
1519
+ разделы `pluginManager` (встроенная панель плагинов), `settings.account`, семейство
1520
+ `settings.agentLoop`/`settings.shell`/`settings.subagent`/`settings.webSearch`,
1521
+ `sidebarBrowser`, `sidebarExcel`, `sidebarOffice`, `voice-input`.
1522
+
1523
+ ## [0.1.3] — 2026-09-20
1524
+
1525
+ Догнал `dsh 0.1.6-alpha.1`: 139 ключей добавлено, 7 новых разделов, 28 устаревших ключей
1526
+ удалено.
1527
+
1528
+ ## [0.1.2] — 2026-09-19
1529
+
1530
+ Добавлены разделы `open-in-app`, `sidebarFiles`, `sidebarRight`, `sidebarCodePreview`.
1531
+
1532
+ ## [0.1.1] — 2026-09-18
1533
+
1534
+ Первый публичный выпуск: регистрация языка `ru` и словари по namespace.