dsh-modern-web-guidance 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  </p>
11
11
 
12
12
  <p align="center">
13
- 208 Baseline-aware guides · 3 tools · 2 skills · zero dependencies · local counters only
13
+ 208 Baseline-aware guides · 4 tools · 2 skills · zero dependencies · local counters only
14
14
  </p>
15
15
 
16
16
  <p align="center">
@@ -63,9 +63,15 @@ calls. Usage counters have existed since 0.2.0, but they never leave the machine
63
63
  | Tool | `web_guidance_search` | Ranked guide ids for a 2–5 word capability query, optional category filter |
64
64
  | Tool | `web_guidance_retrieve` | Full markdown for one or more guide ids (max 10 guides / 120k characters per call) |
65
65
  | Tool | `web_guidance_list` | Catalog overview: counts per category, or ids/titles for one category |
66
+ | Tool | `web_guidance_legacy_scan` | Regex marker scan of explicit paths for legacy patterns; reports findings, changes nothing |
66
67
  | Skill | `modern-web-guidance` | The workflow: search → retrieve → implement → verify → disclose |
67
68
  | Skill | `chrome-extensions` | Manifest V3 engineering and Chrome Web Store submission guidance |
68
69
 
70
+ The scanner is report-only. `web_guidance_legacy_scan` takes `paths` (1–16 files or directories, no default target), an
71
+ optional `rules` list of up to 33 rule ids, and `format: 'summary' | 'json'`; it reads at most 200 files, 512 kB per file
72
+ and 8 MB per call, and returns at most 200 findings. It carries 33 rules, 30 of them upstream's; the
73
+ [parity note](docs/notes/legacy-scan-parity.md) accounts for what is still missing and for the one real debt.
74
+
69
75
  The catalog covers `css`, `ui-atoms`, `ui-components`, `ui-behaviors`, `forms`, `html`, `js`, `accessibility`,
70
76
  `visual-design`, `security`, `performance`, `built-in-ai`, `pwa`, `wasm`, `webmcp`, `privacy`, and `user-experience`.
71
77
 
@@ -112,6 +118,7 @@ reach for it. There are three ways to work with it.
112
118
  | "show me the guide" | `web_guidance_retrieve` | full markdown, up to 10 guides or 120 000 characters per call |
113
119
  | "what is even in here?" | `web_guidance_list` | counts per category, or ids and titles for one category |
114
120
  | "is the catalog actually being used?" | `/guidance-stats` | the local report as text; `--json` is what the panel reads |
121
+ | "will the code I am about to write or review use a legacy pattern?" | `web_guidance_legacy_scan` | findings for the paths you name explicitly, up to 200 per call |
115
122
 
116
123
  ### 1. Just ask for web work (default)
117
124
 
@@ -175,17 +182,17 @@ and security budget. The honest split:
175
182
 
176
183
  | | Google `modern-web-guidance` | This plugin |
177
184
  | --- | --- | --- |
178
- | Shape | npm package: a CLI plus skills, run as `npx -y modern-web-guidance@latest …` | DSH plugin: 3 tools, 2 skills, a sidebar panel |
185
+ | Shape | npm package: a CLI plus skills, run as `npx -y modern-web-guidance@latest …` | DSH plugin: 4 tools, 2 skills, a sidebar panel |
179
186
  | Audience | any agent that can run a shell command | DeepSeek Harness sessions |
180
187
  | Guide corpus | 208 indexed guides (209 `.md` files, one byte-identical duplicate) | the same 208, byte-identical, snapshot `2026_11_09-5bcc021b` |
181
188
  | Lookup | precomputed assets in `gen/` — quantized word vectors (`query-words.i8`, 7.47 MB) and chunk embeddings, 9.16 MB together | BM25 built in-process from `guides.json` and the guide bodies: no assets, no model download (measured 29/30 top-1 on a 30-query probe) |
182
189
  | Runtime dependencies | Node plus the CLI engines shipped in the package | none |
183
190
  | Network | `npx` fetches the package, then works locally | never — the corpus is inside the package |
184
- | Legacy scan | a 4.99 MB `legacy-scan` engine with a four-step scan → verify → report → modernize workflow | not shipped; planned as a dependency-free, report-only regex scanner in 0.5.0 |
191
+ | Legacy scan | a 4.99 MB `legacy-scan` engine with a four-step scan → verify → report → modernize workflow | shipped since 0.5.0: a dependency-free regex scanner over 33 rules, 30 of them upstream's, explicit paths only, report only |
185
192
  | Experimental guides | gated inside the CLI | `[experimental]` marker in `web_guidance_search`, warning banner in `web_guidance_retrieve` |
186
193
  | Usage insight | none | local counters, `/guidance-stats`, and the sidebar panel |
187
194
  | Languages | English guides | the same guides; docs in EN/RU/ZH, panel in EN/RU/ZH |
188
- | Size | — | 254 files, 854 kB packed, 2.5 MB unpacked |
195
+ | Size | — | 256 files, 874 kB packed, 2.5 MB unpacked |
189
196
 
190
197
  What this means in practice: if you work outside DeepSeek Harness, use their package — it is the
191
198
  reference implementation and it owns the corpus. Inside Harness you want the tools in the model's tool
@@ -276,9 +283,9 @@ sections, and states the trust boundary — retrieved guides are reference mater
276
283
  The corpus is third-party text that ends up in a model's context, and tool arguments come from that model, so both
277
284
  sides are bounded:
278
285
 
279
- - **No network, no execution, one local write.** The plugin imports only local `node:fs`, `node:path` and `node:url`; it
280
- has no dependencies and no lifecycle scripts, so nothing is fetched or run at install time. The only file it writes is
281
- its own counters file (see [Usage stats](#usage-stats)).
286
+ - **No network, no execution, one local write.** The plugin imports only local `node:fs`, `node:fs/promises`, `node:path`,
287
+ `node:url` and `node:buffer`; it has no dependencies and no lifecycle scripts, so nothing is fetched or run at install
288
+ time. The only file it writes is its own counters file (see [Usage stats](#usage-stats)).
282
289
  - **Guides cannot smuggle commands.** Upstream's guide bodies cross-reference each other with
283
290
  `npx -y modern-web-guidance@latest retrieve "…"` (197 occurrences in 64 guides). Those references are rewritten to
284
291
  `web_guidance_retrieve({"ids":["…"]})` when the index loads, so nothing a model reads from the catalog asks it to
@@ -288,12 +295,17 @@ sides are bounded:
288
295
  Retrieving all 208 guides would hand back 1.9 MB (~475k tokens) in one result.
289
296
  - **`web_guidance_search` is bounded**: the query is capped at 512 characters and 24 distinct terms before the lexical
290
297
  scan. A 137 KB query used to block the host's event loop for seconds; the same shape now costs milliseconds.
298
+ - **The scanner reads only what it is given.** `web_guidance_legacy_scan` has no default target: the caller names 1–16
299
+ paths, each must resolve inside the session workspace, and a symlink that escapes it is skipped rather than followed.
300
+ It refuses absolute paths, paths outside the workspace and missing paths (`path-absolute`, `path-outside`,
301
+ `path-missing`), caps the read at 200 files, 512 kB per file and 8 MB per call, and never writes a byte.
291
302
  - **The catalog cannot reach outside the package**: entry ids and categories must be slugs, and every resolved path is
292
303
  checked to stay inside the data directory. A tampered `guides.json` used to be able to read any `*.md` on disk.
293
304
  - **Failures retry and stay opaque**: a transient read error no longer poisons the tool for the session, and tool errors
294
305
  no longer disclose absolute install paths.
295
306
 
296
- Both properties are covered by `test/hardening.test.mjs`, which fails on the pre-hardening code. The full audit — three
307
+ These properties are covered by `test/hardening.test.mjs`, which fails on the pre-hardening code, and by
308
+ `test/scan-core.test.mjs` for the scanner's containment and caps. The full audit — three
297
309
  reports plus runnable repros — lives in [`audit/`](audit/), and the repro scripts under `audit/repro/` exit non-zero
298
310
  while a defect is live.
299
311
 
@@ -301,7 +313,7 @@ while a defect is live.
301
313
 
302
314
  ## 📊 Usage stats
303
315
 
304
- Since 0.2.0 the plugin records what the three tools are actually asked for. The counters are local: no endpoint, no
316
+ Since 0.2.0 the plugin records what the four tools are actually asked for. The counters are local: no endpoint, no
305
317
  analytics service, no third-party SDK, and nothing leaves the machine.
306
318
 
307
319
  The report is a human slash command, not a tool the agent can call:
@@ -317,6 +329,8 @@ The report is a human slash command, not a tool the agent can call:
317
329
  counts, per-category hit counts, per-guide retrieve counts, and the last 1000 calls with their query text and returned
318
330
  ids. Misses are the point — a count of misses that cannot be reproduced cannot be fixed.
319
331
 
332
+ 0.5.0 adds a fourth row (`legacy-scan`) and a findings total to the same report.
333
+
320
334
  Where it lives: the profile's durable storage, the same trust boundary that already holds session logs. Retention
321
335
  defaults to 90 days, and `--reset --yes` erases it.
322
336
 
@@ -327,18 +341,19 @@ Turn it off: set `enabled: false` in the plugin config — no listener is regist
327
341
  ## 🖥 Sidebar panel
328
342
 
329
343
  The plugin adds a client half: a guidance icon in the left rail opens **Guidance Insights** in the central area. It
330
- shows the same counters `/guidance-stats` prints, as a view:
344
+ shows the same counters `/guidance-stats` prints — four tools plus the finding total — as a view:
331
345
 
332
346
  - total calls and hit rate for the last 7 days
333
347
  - the most frequent queries, with hit and miss counts
334
348
  - category coverage — retrieved guides against the 208 in the corpus
335
349
  - the queries that found nothing
336
350
  - how many guides were never retrieved
351
+ - the scanner's finding total for the same window
337
352
 
338
353
  A single refresh control re-reads the counters; there is nothing else to press.
339
354
 
340
355
  The panel is read-only. It never changes a counter, never leaves the machine, and adds no network calls and no new
341
- tools — the same three tools stay exactly three. `/guidance-stats` remains the text and `--json` version for the
356
+ tools — the same four tools stay exactly four. `/guidance-stats` remains the text and `--json` version for the
342
357
  terminal and for the agent; the panel is that report as a view.
343
358
 
344
359
  <a id="configuration"></a>
@@ -383,6 +398,12 @@ dialog", "view transition", "container query"), not sentences. If a query still
383
398
  is volatile in one engine — see [Experimental guides](#experimental-guides). The guide is still the
384
399
  right shape; feature-detect it before shipping.
385
400
 
401
+ **What can the legacy scanner actually do?** It is a regex marker scan, not a parser: 33 rules matched
402
+ line by line, covering 33 of the 208 guides (15.9 %), with no AST, no data-flow analysis and no autofix —
403
+ it reports and stops. It makes no parity claim with Chrome's own `legacy-scan`, a 4.99 MB engine with a
404
+ bundled TypeScript compiler; this one is a dependency-free pass that can produce false positives and
405
+ misses, so treat a finding as a lead to verify, not as a verdict.
406
+
386
407
  **Does anything leave my machine?** No. The corpus is inside the package, search is computed
387
408
  in-process, and there are no network calls. The usage counters are local files and can be switched off
388
409
  with `enabled: false` — see [Security and limits](#security-and-limits) and [ADR 0001](docs/adr/0001-local-telemetry.md).
@@ -401,8 +422,9 @@ work on their own; the panel additionally needs the host's `slots`, `locale` and
401
422
  | Release | State | What it is |
402
423
  | --- | --- | --- |
403
424
  | 0.4.0 | shipped | the upstream v1.0.0 corpus (208 guides) plus experimental marking |
404
- | 0.4.1 | this pass | docs: navigation, the comparison, the catalog map |
405
- | 0.5.0 | next | `legacy-scan` as a fourth tool: 30–35 regex rules ported from upstream's engine, explicit paths only, report-only output. Design and verdict: [docs/notes/legacy-scan-feasibility.md](docs/notes/legacy-scan-feasibility.md) |
425
+ | 0.4.1 | shipped | docs: navigation, the comparison, the catalog map |
426
+ | 0.5.0 | shipped | `legacy-scan` as the fourth tool: 30 regex rules, explicit paths only, report only — `web_guidance_legacy_scan`, with a fourth counter and a findings total in the panel. Design and verdict: [docs/notes/legacy-scan-feasibility.md](docs/notes/legacy-scan-feasibility.md) |
427
+ | 0.6.0 | this release | three more rules (`:host-context()`, text balancing, `select:invalid`) take the table to 33 rules, 30 of them upstream's, and a guide-hash guard keeps a rule honest after a corpus refresh |
406
428
 
407
429
  Deliberately **not** planned: shipping upstream's 9.16 MB embedding tables, or its 4.99 MB
408
430
  `legacy-scan` engine with a bundled TypeScript compiler. Both would break "zero runtime dependencies,
@@ -423,6 +445,7 @@ no network, no code generation" for a gain the lexical index does not show.
423
445
  | [`audit/`](audit) | you want the 0.1.x security, corpus and bug audits as they were written |
424
446
  | [`docs/adr/`](docs/adr) | you want the decisions: local telemetry, and what followed |
425
447
  | [`docs/notes/`](docs/notes) | you want the research behind a release (upstream diffs, feasibility studies, plans) |
448
+ | [`docs/notes/legacy-scan-feasibility.md`](docs/notes/legacy-scan-feasibility.md) | you want the analysis behind `legacy-scan`: what upstream's engine does, what a regex scan can match, and the verdict |
426
449
 
427
450
  <a id="development"></a>
428
451
 
@@ -432,11 +455,12 @@ no network, no code generation" for a gain the lexical index does not show.
432
455
  npm test
433
456
  ```
434
457
 
435
- `npm test` runs the suite: `guidance.test.mjs` covers the index, ranking, retrieval and the stemmer;
436
- `plugin.test.mjs` mounts the plugin against a fake Cordis context; `hardening.test.mjs` guards the
437
- security invariants; `report.test.mjs`, `stats.test.mjs`, `store.test.mjs` and `telemetry.test.mjs`
438
- cover the counter pipeline; `client-locales.test.mjs` keeps the panel's three dictionaries in sync. In a
439
- sandbox that forbids child processes, run the files directly (`node test/hardening.test.mjs`).
458
+ `npm test` runs 150 tests across twelve files: `guidance.test.mjs` covers the index, ranking, retrieval and the stemmer;
459
+ `plugin.test.mjs` mounts the plugin against a fake Cordis context; `hardening.test.mjs` guards the security invariants;
460
+ `report.test.mjs`, `stats.test.mjs`, `store.test.mjs` and `telemetry.test.mjs` cover the counter pipeline;
461
+ `scan-core.test.mjs`, `scan-rules.test.mjs` and `scan-tool.test.mjs` cover the scanner's containment, its rule table and
462
+ the tool contract, and `scan.test.mjs` is the end-to-end scan; `client-locales.test.mjs` keeps the panel's three
463
+ dictionaries in sync. In a sandbox that forbids child processes, run the files directly (`node test/hardening.test.mjs`).
440
464
 
441
465
  <a id="publishing"></a>
442
466
 
package/README.ru.md CHANGED
@@ -10,7 +10,7 @@
10
10
  </p>
11
11
 
12
12
  <p align="center">
13
- 208 гайдов с Baseline-фолбэками · 3 инструмента · 2 скилла · ноль зависимостей · только локальные счётчики
13
+ 208 гайдов с Baseline-фолбэками · 4 инструмента · 2 скилла · ноль зависимостей · только локальные счётчики
14
14
  </p>
15
15
 
16
16
  <p align="center">
@@ -63,9 +63,16 @@ API платформы (нативные `<dialog>`/popover, anchor positioning,
63
63
  | Инструмент | `web_guidance_search` | Ранжированные id гайдов по запросу из 2–5 слов, опционально с фильтром по категории |
64
64
  | Инструмент | `web_guidance_retrieve` | Полный markdown одного или нескольких гайдов (до 10 гайдов / 120k символов за вызов) |
65
65
  | Инструмент | `web_guidance_list` | Обзор каталога: счётчики по категориям или id и заголовки одной категории |
66
+ | Инструмент | `web_guidance_legacy_scan` | Маркерный скан явно перечисленных путей на legacy-паттерны; находит и ничего не меняет |
66
67
  | Скилл | `modern-web-guidance` | Рабочий процесс: найти → достать → реализовать → проверить → раскрыть фолбэк |
67
68
  | Скилл | `chrome-extensions` | Разработка Manifest V3 и требования Chrome Web Store |
68
69
 
70
+ Сканер работает только на чтение. `web_guidance_legacy_scan` принимает `paths` (1–16 файлов или каталогов, цели по
71
+ умолчанию нет), необязательный список `rules` до 30 id правил и `format: 'summary' | 'json'`; за вызов он читает не
72
+ более 200 файлов, до 512 kB на файл и 8 MB суммарно, и возвращает не более 200 находок. В таблице 33 правила, из них
73
+ 30 — правила апстрима; чего до сих пор не хватает и в чём единственный настоящий долг — в
74
+ [заметке о соответствии](docs/notes/legacy-scan-parity.md).
75
+
69
76
  Каталог покрывает `css`, `ui-atoms`, `ui-components`, `ui-behaviors`, `forms`, `html`, `js`, `accessibility`,
70
77
  `visual-design`, `security`, `performance`, `built-in-ai`, `pwa`, `wasm`, `webmcp`, `privacy` и `user-experience`.
71
78
 
@@ -101,7 +108,9 @@ dsh plugin --profile desktop add file:E:/path/to/dsh-modern-web-guidance
101
108
  Ничего настраивать не нужно: плагин регистрируется сам, а скилл `modern-web-guidance` подсказывает агенту, когда к
102
109
  нему идти. Есть три способа работы.
103
110
 
104
- ### <a id="which-tool-when"></a>Каким инструментом когда
111
+ <a id="which-tool-when"></a>
112
+
113
+ ### Каким инструментом когда
105
114
 
106
115
  | Что нужно | Вызов | Что вернётся |
107
116
  | --- | --- | --- |
@@ -109,6 +118,7 @@ dsh plugin --profile desktop add file:E:/path/to/dsh-modern-web-guidance
109
118
  | «покажи гайд» | `web_guidance_retrieve` | полный markdown, до 10 гайдов или 120 000 символов за вызов |
110
119
  | «что там вообще есть?» | `web_guidance_list` | счётчики по категориям или id и заголовки одной категории |
111
120
  | «каталог вообще используется?» | `/guidance-stats` | локальный отчёт текстом; `--json` — это то, что читает панель |
121
+ | «код, который я собираюсь написать или проверить, не тянет legacy-паттерн?» | `web_guidance_legacy_scan` | находки по явно перечисленным путям, до 200 за вызов |
112
122
 
113
123
  ### 1. Просто попросить сделать веб-задачу (обычный режим)
114
124
 
@@ -171,17 +181,17 @@ DeepSeek Harness: инструменты для модели, боковую п
171
181
 
172
182
  | | Google `modern-web-guidance` | Этот плагин |
173
183
  | --- | --- | --- |
174
- | Форма | npm-пакет: CLI плюс скиллы, запуск как `npx -y modern-web-guidance@latest …` | плагин DSH: 3 инструмента, 2 скилла, боковая панель |
184
+ | Форма | npm-пакет: CLI плюс скиллы, запуск как `npx -y modern-web-guidance@latest …` | плагин DSH: 4 инструмента, 2 скилла, боковая панель |
175
185
  | Аудитория | любой агент, умеющий запускать команды в оболочке | сессии DeepSeek Harness |
176
186
  | Корпус гайдов | 208 проиндексированных гайдов (209 файлов `.md`, один побайтовый дубль) | те же 208, побайтово идентичны, срез `2026_11_09-5bcc021b` |
177
187
  | Поиск | предвычисленные ассеты в `gen/` — квантованные векторы слов (`query-words.i8`, 7.47 MB) и эмбеддинги чанков, 9.16 MB вместе | BM25 строится в процессе из `guides.json` и тел гайдов: без ассетов, без скачивания модели (измерено 29/30 top-1 на пробе из 30 запросов) |
178
188
  | Зависимости рантайма | Node плюс движки CLI, поставляемые в пакете | нет |
179
189
  | Сеть | `npx` скачивает пакет, дальше всё локально | никогда — корпус лежит внутри пакета |
180
- | Сканер legacy | движок `legacy-scan` на 4.99 MB с четырёхшаговым процессом scan → verify → report → modernize | не поставляется; запланирован на 0.5.0 как сканер регулярными выражениями без зависимостей, только с отчётом |
190
+ | Сканер legacy | движок `legacy-scan` на 4.99 MB с четырёхшаговым процессом scan → verify → report → modernize | поставляется с 0.5.0: сканер регулярными выражениями без зависимостей, 33 правила, из них 30 — правила апстрима, только явные пути, только отчёт |
181
191
  | Экспериментальные гайды | закрыты внутри CLI | маркер `[experimental]` в `web_guidance_search`, предупреждающий баннер в `web_guidance_retrieve` |
182
192
  | Анализ использования | нет | локальные счётчики, `/guidance-stats` и боковая панель |
183
193
  | Языки | гайды на английском | те же гайды; документация на EN/RU/ZH, панель на EN/RU/ZH |
184
- | Размер | — | 254 файла, 854 kB в упаковке, 2.5 MB распакованного |
194
+ | Размер | — | 256 файлов, 874 kB в упаковке, 2.5 MB распакованного |
185
195
 
186
196
  Что это значит на практике: если вы работаете вне DeepSeek Harness, берите их пакет — это референсная реализация, и
187
197
  корпус принадлежит им. Внутри Harness нужны инструменты в списке инструментов модели, установка без сборки и без сети и
@@ -271,9 +281,9 @@ CLI через `npx`, сохраняет заметки про фреймвор
271
281
  Корпус — это сторонний текст, который попадает в контекст модели, а аргументы инструментов приходят от самой модели,
272
282
  поэтому ограничены обе стороны:
273
283
 
274
- - **Ни сети, ни исполнения, одна локальная запись.** Плагин импортирует только локальные `node:fs`, `node:path` и
275
- `node:url`; у него нет зависимостей и lifecycle-скриптов, поэтому при установке ничего не скачивается и не
276
- выполняется. Единственный файл, который он пишет, — собственный файл счётчиков (см.
284
+ - **Ни сети, ни исполнения, одна локальная запись.** Плагин импортирует только локальные `node:fs`, `node:fs/promises`,
285
+ `node:path`, `node:url` и `node:buffer`; у него нет зависимостей и lifecycle-скриптов, поэтому при установке ничего
286
+ не скачивается и не выполняется. Единственный файл, который он пишет, — собственный файл счётчиков (см.
277
287
  [Статистика использования](#usage-stats)).
278
288
  - **Гайды не могут протащить команды.** В телах гайдов апстрима есть перекрёстные ссылки вида
279
289
  `npx -y modern-web-guidance@latest retrieve "…"` (197 вхождений в 64 гайдах). При загрузке индекса они
@@ -284,20 +294,24 @@ CLI через `npx`, сохраняет заметки про фреймвор
284
294
  отдельно. Извлечение всех 208 гайдов отдало бы 1.9 MB (~475k токенов) одним результатом.
285
295
  - **`web_guidance_search` ограничен**: запрос обрезается до 512 символов и 24 уникальных термов до лексического
286
296
  прохода. Запрос на 137 KB раньше блокировал event loop хоста на секунды; та же форма теперь стоит миллисекунды.
297
+ - **Сканер читает только то, что ему дали.** У `web_guidance_legacy_scan` нет цели по умолчанию: вызывающий называет
298
+ 1–16 путей, каждый обязан разрешаться внутри воркспейса сессии, а симлинк, уводящий наружу, пропускается, а не
299
+ читается. Абсолютные пути, пути за пределами воркспейса и отсутствующие пути он отклоняет (`path-absolute`,
300
+ `path-outside`, `path-missing`), чтение ограничено 200 файлами, 512 kB на файл и 8 MB на вызов, и он не пишет ни байта.
287
301
  - **Каталог не может выйти за пределы пакета**: id и категории записей обязаны быть слагами, и каждый разрешённый путь
288
302
  проверяется на принадлежность каталогу данных. Подменённый `guides.json` раньше мог прочитать любой `*.md` на диске.
289
303
  - **Сбои повторяются и не раскрывают лишнего**: случайная ошибка чтения больше не отравляет инструмент на всю сессию,
290
304
  а тексты ошибок не выдают абсолютные пути установки.
291
305
 
292
- Оба свойства закреплены в `test/hardening.test.mjs`, который падает на коде до этих правок. Полный аудит — три отчёта
293
- и запускаемые репро — лежит в [`audit/`](audit/); скрипты в `audit/repro/` завершаются с ненулевым кодом, пока дефект
294
- жив.
306
+ Эти свойства закреплены в `test/hardening.test.mjs`, который падает на коде до этих правок, а изоляция и лимиты
307
+ сканера — в `test/scan-core.test.mjs`. Полный аудит — три отчёта и запускаемые репро — лежит в [`audit/`](audit/);
308
+ скрипты в `audit/repro/` завершаются с ненулевым кодом, пока дефект жив.
295
309
 
296
310
  <a id="usage-stats"></a>
297
311
 
298
312
  ## 📊 Статистика использования
299
313
 
300
- С версии 0.2.0 плагин записывает, что именно спрашивают у трёх инструментов. Счётчики локальные: ни endpoint, ни аналитического
314
+ С версии 0.2.0 плагин записывает, что именно спрашивают у четырёх инструментов. Счётчики локальные: ни endpoint, ни аналитического
301
315
  сервиса, ни стороннего SDK — за пределы машины ничего не уходит.
302
316
 
303
317
  Отчёт смотрит человек командой, а не инструментом, который может вызвать агент:
@@ -313,6 +327,8 @@ CLI через `npx`, сохраняет заметки про фреймвор
313
327
  счётчики по дням, попадания по категориям, извлечения по гайдам и последние 1000 вызовов с текстом запроса и
314
328
  возвращёнными id. Промахи как раз и нужны — промах, который нельзя воспроизвести, нельзя исправить.
315
329
 
330
+ 0.5.0 добавляет в тот же отчёт четвёртую строку (`legacy-scan`) и итог по находкам.
331
+
316
332
  Где лежит: durable-хранилище профиля — та же граница доверия, где уже лежат логи сессий. Срок хранения по умолчанию
317
333
  90 дней, `--reset --yes` стирает всё.
318
334
 
@@ -323,19 +339,21 @@ CLI через `npx`, сохраняет заметки про фреймвор
323
339
  ## 🖥 Боковая панель
324
340
 
325
341
  Плагин добавляет клиентскую часть: иконка в левом рейле открывает в центральной области панель
326
- **Guidance Insights**. Это те же счётчики, что печатает `/guidance-stats`, только в виде экрана:
342
+ **Guidance Insights**. Это те же счётчики, что печатает `/guidance-stats` (четыре инструмента и итог по находкам),
343
+ только в виде экрана:
327
344
 
328
345
  - вызовы и доля попаданий за последние 7 дней
329
346
  - самые частые запросы с числом попаданий и промахов
330
347
  - покрытие категорий — извлечённые гайды против 208 в корпусе
331
348
  - запросы, которые ничего не нашли
332
349
  - сколько гайдов не извлекали ни разу
350
+ - итог по находкам сканера за то же окно
333
351
 
334
352
  Единственный элемент управления — обновление: одна кнопка перечитывает счётчики. Интерфейс панели переведён на
335
353
  английский, русский и китайский.
336
354
 
337
355
  Панель только читает. Она не меняет счётчики, не покидает машину и не добавляет ни сетевых вызовов, ни новых
338
- инструментов — три инструмента остаются ровно тремя. `/guidance-stats` остаётся текстовой версией (в том числе
356
+ инструментов — четыре инструмента остаются ровно четырьмя. `/guidance-stats` остаётся текстовой версией (в том числе
339
357
  `--json`) для терминала и агента; панель — тот же отчёт в виде экрана.
340
358
 
341
359
  <a id="configuration"></a>
@@ -380,6 +398,12 @@ CLI через `npx`, сохраняет заметки про фреймвор
380
398
  движке — см. [Экспериментальные гайды](#experimental-guides). Гайд всё равно задаёт правильную форму; проверьте её
381
399
  фича-детектом, прежде чем отгружать.
382
400
 
401
+ **Что сканер legacy вообще умеет?** Это маркерный скан регулярными выражениями, а не парсер: 33 правила проверяются
402
+ построчно и покрывают 33 из 208 гайдов (15,9 %), без AST, без анализа потока данных и без автоисправлений — он
403
+ отчитывается и останавливается. Паритета с собственным `legacy-scan` Chrome он не заявляет: там движок на 4.99 MB со
404
+ встроенным компилятором TypeScript, здесь — проход без зависимостей, который может и ошибиться, и пропустить; считайте
405
+ находку зацепкой для проверки, а не вердиктом.
406
+
383
407
  **Что-нибудь уходит с моей машины?** Нет. Корпус лежит внутри пакета, поиск считается в процессе, сетевых вызовов нет.
384
408
  Счётчики использования — локальные файлы, их можно выключить через `enabled: false` — см.
385
409
  [Безопасность и границы](#security-and-limits) и [ADR 0001](docs/adr/0001-local-telemetry.md).
@@ -398,8 +422,9 @@ CLI через `npx`, сохраняет заметки про фреймвор
398
422
  | Выпуск | Состояние | Что это |
399
423
  | --- | --- | --- |
400
424
  | 0.4.0 | вышел | корпус апстрима v1.0.0 (208 гайдов) плюс пометка экспериментальных |
401
- | 0.4.1 | этот проход | документация: навигация, сравнение, карта каталога |
402
- | 0.5.0 | следующий | `legacy-scan` четвёртым инструментом: 30–35 правил-регулярок, перенесённых из движка апстрима, только явные пути, только отчёт. Дизайн и вердикт: [docs/notes/legacy-scan-feasibility.md](docs/notes/legacy-scan-feasibility.md) |
425
+ | 0.4.1 | вышел | документация: навигация, сравнение, карта каталога |
426
+ | 0.5.0 | вышел | `legacy-scan` четвёртым инструментом: 30 правил-регулярок, только явные пути, только отчёт — `web_guidance_legacy_scan`, с четвёртым счётчиком и итогом по находкам в панели. Дизайн и вердикт: [docs/notes/legacy-scan-feasibility.md](docs/notes/legacy-scan-feasibility.md) |
427
+ | 0.6.0 | этот выпуск | ещё три правила (`:host-context()`, балансировка текста, `select:invalid`) — в таблице 33 правила, 30 из них апстрима, плюс страховка по хешам гайдов: после обновления корпуса правило не устареет молча |
403
428
 
404
429
  Намеренно **не** планируется: поставлять таблицы эмбеддингов апстрима на 9.16 MB или его движок `legacy-scan` на
405
430
  4.99 MB со встроенным компилятором TypeScript. И то и другое сломало бы «ноль зависимостей рантайма, никакой сети,
@@ -420,6 +445,7 @@ CLI через `npx`, сохраняет заметки про фреймвор
420
445
  | [`audit/`](audit) | нужны аудиты безопасности, корпуса и багов 0.1.x в том виде, как их писали |
421
446
  | [`docs/adr/`](docs/adr) | нужны решения: локальная телеметрия и что за ней последовало |
422
447
  | [`docs/notes/`](docs/notes) | нужно исследование, стоящее за релизом (диффы апстрима, feasibility-исследования, планы) |
448
+ | [`docs/notes/legacy-scan-feasibility.md`](docs/notes/legacy-scan-feasibility.md) | нужен разбор, стоящий за `legacy-scan`: что делает движок апстрима, что может поймать скан регулярками и каков вердикт |
423
449
 
424
450
  <a id="development"></a>
425
451
 
@@ -429,11 +455,12 @@ CLI через `npx`, сохраняет заметки про фреймвор
429
455
  npm test
430
456
  ```
431
457
 
432
- `npm test` запускает весь набор: `guidance.test.mjs` покрывает индекс, ранжирование, извлечение и стеммер;
433
- `plugin.test.mjs` монтирует плагин на поддельном контексте Cordis; `hardening.test.mjs` защищает инварианты
458
+ `npm test` запускает 150 тестов в двенадцати файлах: `guidance.test.mjs` покрывает индекс, ранжирование, извлечение и
459
+ стеммер; `plugin.test.mjs` монтирует плагин на поддельном контексте Cordis; `hardening.test.mjs` защищает инварианты
434
460
  безопасности; `report.test.mjs`, `stats.test.mjs`, `store.test.mjs` и `telemetry.test.mjs` покрывают конвейер
435
- счётчиков; `client-locales.test.mjs` держит синхронными три словаря панели. В песочнице, где запрещены дочерние
436
- процессы, запускайте файлы напрямую (`node test/hardening.test.mjs`).
461
+ счётчиков; `scan-core.test.mjs`, `scan-rules.test.mjs` и `scan-tool.test.mjs` покрывают изоляцию сканера, его таблицу
462
+ правил и контракт инструмента, а `scan.test.mjs` — сквозной скан; `client-locales.test.mjs` держит синхронными три
463
+ словаря панели. В песочнице, где запрещены дочерние процессы, запускайте файлы напрямую (`node test/hardening.test.mjs`).
437
464
 
438
465
  <a id="publishing"></a>
439
466
 
package/README.zh.md CHANGED
@@ -10,7 +10,7 @@
10
10
  </p>
11
11
 
12
12
  <p align="center">
13
- 208 篇带 Baseline 降级方案的指南 · 3 个工具 · 2 个技能 · 零依赖 · 仅本地统计
13
+ 208 篇带 Baseline 降级方案的指南 · 4 个工具 · 2 个技能 · 零依赖 · 仅本地统计
14
14
  </p>
15
15
 
16
16
  <p align="center">
@@ -62,9 +62,14 @@
62
62
  | 工具 | `web_guidance_search` | 用 2–5 个词描述能力,返回排好序的指南 id,可按分类过滤 |
63
63
  | 工具 | `web_guidance_retrieve` | 返回一篇或多篇指南的完整 markdown(每次调用最多 10 篇 / 120k 字符) |
64
64
  | 工具 | `web_guidance_list` | 目录总览:各分类数量,或某个分类下的 id 与标题 |
65
+ | 工具 | `web_guidance_legacy_scan` | 对显式给出的路径做过时模式(legacy)的正则标记扫描;只报告,不改动任何东西 |
65
66
  | 技能 | `modern-web-guidance` | 工作流:检索 → 取回 → 实现 → 校验 → 说明降级方案 |
66
67
  | 技能 | `chrome-extensions` | Manifest V3 开发与 Chrome Web Store 上架要求 |
67
68
 
69
+ 扫描器只读不写。`web_guidance_legacy_scan` 接受 `paths`(1–16 个文件或目录,没有默认目标)、可选的 `rules`(最多
70
+ 30 个规则 id)和 `format: 'summary' | 'json'`;每次调用最多读 200 个文件、单文件 512 kB、合计 8 MB,最多返回
71
+ 200 条发现。表里一共 33 条规则,覆盖上游 35 条中的 30 条;剩下没覆盖的规则、以及唯一真正的一笔技术债,都记在[对照说明](docs/notes/legacy-scan-parity.md)里。
72
+
68
73
  目录覆盖 `css`、`ui-atoms`、`ui-components`、`ui-behaviors`、`forms`、`html`、`js`、`accessibility`、
69
74
  `visual-design`、`security`、`performance`、`built-in-ai`、`pwa`、`wasm`、`webmcp`、`privacy` 和
70
75
  `user-experience`。
@@ -110,6 +115,7 @@ bundle 只插入一行(`modern-web-guidance`),不需要构建步骤、依
110
115
  | “把指南给我看” | `web_guidance_retrieve` | 完整 markdown,每次最多 10 篇或 120 000 字符 |
111
116
  | “这里到底有什么?” | `web_guidance_list` | 各分类数量,或某个分类下的 id 与标题 |
112
117
  | “目录真的有人在用吗?” | `/guidance-stats` | 文本形式的本地报告;`--json` 是面板读取的格式 |
118
+ | “我正要写或要审的代码会不会用上过时写法?” | `web_guidance_legacy_scan` | 对显式列出的路径给出发现,每次最多 200 条 |
113
119
 
114
120
  ### 1. 直接提出 Web 开发需求(默认方式)
115
121
 
@@ -170,17 +176,17 @@ web_guidance_search({"query": "view transition"}) → 取回之前的排序候
170
176
 
171
177
  | | Google `modern-web-guidance` | 本插件 |
172
178
  | --- | --- | --- |
173
- | 形态 | npm 包:一个 CLI 加技能,用 `npx -y modern-web-guidance@latest …` 运行 | DSH 插件:3 个工具、2 个技能、一个侧边栏面板 |
179
+ | 形态 | npm 包:一个 CLI 加技能,用 `npx -y modern-web-guidance@latest …` 运行 | DSH 插件:4 个工具、2 个技能、一个侧边栏面板 |
174
180
  | 受众 | 任何能执行 shell 命令的智能体 | DeepSeek Harness 会话 |
175
181
  | 指南语料 | 208 篇已索引的指南(209 个 `.md` 文件,其中一个是逐字节副本) | 同样的 208 篇,逐字节一致,快照 `2026_11_09-5bcc021b` |
176
182
  | 检索方式 | `gen/` 里预计算好的资源 —— 量化词向量(`query-words.i8`,7.47 MB)与分块 embedding,合计 9.16 MB | 在进程内用 `guides.json` 和指南正文构建 BM25:没有资源文件,不下载模型(在 30 条查询的探针上测得 29/30 的 top-1) |
177
183
  | 运行时依赖 | Node 加上包里附带的 CLI 引擎 | 无 |
178
184
  | 网络 | `npx` 先取包,之后本地运行 | 从不联网 —— 语料就在包里 |
179
- | legacy-scan 扫描 | 4.99 MB 的 `legacy-scan` 引擎,带扫描 → 校验 → 报告 → 现代化四步工作流 | 未内置;计划在 0.5.0 里做成零依赖、只输出报告的 regex 扫描器 |
185
+ | legacy-scan 扫描 | 4.99 MB 的 `legacy-scan` 引擎,带扫描 → 校验 → 报告 → 现代化四步工作流 | 0.5.0 起内置:零依赖的正则扫描器,33 条规则(其中 30 条对应上游规则),只接受显式路径,只输出报告 |
180
186
  | 实验性指南 | 在 CLI 内部做门控 | `web_guidance_search` 结果里的 `[experimental]` 标记,`web_guidance_retrieve` 开头的警告横幅 |
181
187
  | 使用情况洞察 | 无 | 本地计数、`/guidance-stats` 和侧边栏面板 |
182
188
  | 语言 | 英文指南 | 同样的指南;文档 EN/RU/ZH,面板 EN/RU/ZH |
183
- | 体积 | — | 254 个文件,打包 854 kB,解包 2.5 MB |
189
+ | 体积 | — | 256 个文件,打包 874 kB,解包 2.5 MB |
184
190
 
185
191
  实际意味着:如果你在 DeepSeek Harness 之外工作,就用他们的包 —— 它是参考实现,语料也归它所有。在 Harness
186
192
  里面,你要的是工具出现在模型的工具列表里、安装不需要构建步骤也不联网,以及一个能看出指南到底有没有人在用的
@@ -262,8 +268,9 @@ web_guidance_search({"query": "view transition"}) → 取回之前的排序候
262
268
 
263
269
  语料是要进入模型上下文的第三方文本,而工具参数又来自该模型,所以两边都设了边界:
264
270
 
265
- - **无网络、无执行,只有一次本地写入。** 插件只导入本地的 `node:fs`、`node:path` 和 `node:url`;没有依赖,也没有
266
- lifecycle 脚本,因此安装时不会下载或运行任何东西。它唯一写入的文件是自己的计数文件(见[使用统计](#usage-stats))。
271
+ - **无网络、无执行,只有一次本地写入。** 插件只导入本地的 `node:fs`、`node:fs/promises`、`node:path`、`node:url`
272
+ 和 `node:buffer`;没有依赖,也没有 lifecycle 脚本,因此安装时不会下载或运行任何东西。它唯一写入的文件是自己的
273
+ 计数文件(见[使用统计](#usage-stats))。
267
274
  - **指南里夹带不了命令。** 上游指南正文之间用
268
275
  `npx -y modern-web-guidance@latest retrieve "…"` 互相引用(197 处,分布在 64 篇指南里)。建立索引时这些引用会被
269
276
  改写成 `web_guidance_retrieve({"ids":["…"]})`,所以模型从目录里读到的内容不会要求它安装并执行一个未锁定版本的
@@ -272,19 +279,24 @@ web_guidance_search({"query": "view transition"}) → 取回之前的排序候
272
279
  个;渲染器会明确说明哪些没有返回、需要单独请求。一次取回全部 208 篇会返回 1.9 MB(约 475k token)。
273
280
  - **`web_guidance_search` 有上限**:在进入词法扫描前,查询被截断到 512 字符和 24 个不同词项。此前一个 137 KB
274
281
  的查询会阻塞宿主事件循环数秒;同样形状的查询现在只需毫秒级。
282
+ - **扫描器只读它被明确给定的内容。** `web_guidance_legacy_scan` 没有默认目标:调用方要给出 1–16 个路径,每个路径
283
+ 都必须落在会话工作区内,指向区外的符号链接会被跳过而不是跟随。绝对路径、工作区外的路径和不存在路径会被拒绝
284
+ (`path-absolute`、`path-outside`、`path-missing`),读取上限为 200 个文件、单文件 512 kB、每次 8 MB,并且它不写
285
+ 任何一个字节。
275
286
  - **目录无法越出包外**:条目 id 与分类必须是 slug,每个解析出的路径都会校验是否位于数据目录内。被篡改的
276
287
  `guides.json` 此前可以读取磁盘上任意 `*.md`。
277
288
  - **失败会重试,且不泄露信息**:一次临时的读取错误不再让工具在本次会话中永久失效,错误信息也不再暴露安装的
278
289
  绝对路径。
279
290
 
280
- 这两类性质由 `test/hardening.test.mjs` 覆盖,该文件在加固前的代码上会失败。完整审计 —— 三份报告与可运行的复现
291
+ 这些性质由 `test/hardening.test.mjs` 覆盖(该文件在加固前的代码上会失败),扫描器的隔离与上限则由
292
+ `test/scan-core.test.mjs` 覆盖。完整审计 —— 三份报告与可运行的复现
281
293
  脚本 —— 位于 [`audit/`](audit/);只要缺陷仍然存在,`audit/repro/` 下的脚本就会以非零码退出。
282
294
 
283
295
  <a id="usage-stats"></a>
284
296
 
285
297
  ## 📊 使用统计
286
298
 
287
- 自 0.2.0 起,插件记录三个工具到底被问了什么。计数只在本地:没有上报端点、没有分析服务、没有第三方 SDK,任何东西都不离开这台
299
+ 自 0.2.0 起,插件记录四个工具到底被问了什么。计数只在本地:没有上报端点、没有分析服务、没有第三方 SDK,任何东西都不离开这台
288
300
  机器。
289
301
 
290
302
  查看用的是给人用的斜杠命令,不是智能体能调用的工具:
@@ -300,6 +312,8 @@ web_guidance_search({"query": "view transition"}) → 取回之前的排序候
300
312
  命中次数、各指南被取回次数,以及最近 1000 次调用携带的查询文本和返回的 id。未命中的查询才是重点 —— 无法复现的
301
313
  未命中就无法修复。
302
314
 
315
+ 0.5.0 在同一份报告里加了第四行(`legacy-scan`)和一个发现总数。
316
+
303
317
  存放位置:profile 的持久存储,也就是本来就存会话日志的那层信任边界。默认保留 90 天,`--reset --yes` 会立即清空。
304
318
 
305
319
  关闭方式:在插件配置里设 `enabled: false` —— 此时不注册监听,也不写文件。
@@ -309,17 +323,18 @@ web_guidance_search({"query": "view transition"}) → 取回之前的排序候
309
323
  ## 🖥 侧边栏面板
310
324
 
311
325
  插件增加了客户端部分:左侧栏的图标会在中央区域打开 **Guidance Insights** 面板。它展示的就是
312
- `/guidance-stats` 打印的那些计数,只是换成了界面:
326
+ `/guidance-stats` 打印的那些计数(四个工具加发现总数),只是换成了界面:
313
327
 
314
328
  - 最近 7 天的调用次数与命中率
315
329
  - 最常出现的查询,以及各自的命中与未命中次数
316
330
  - 分类覆盖情况 —— 已取回的指南对 208 篇语料
317
331
  - 什么都没找到的查询
318
332
  - 有多少篇指南从未被取回过
333
+ - 同一时间窗口内扫描器的发现总数
319
334
 
320
335
  操作只有一个刷新:重新读取一次计数。
321
336
 
322
- 面板是只读的。它不改动计数、不离开这台机器,也不新增网络请求和工具 —— 三个工具仍然是三个。
337
+ 面板是只读的。它不改动计数、不离开这台机器,也不新增网络请求和工具 —— 四个工具仍然是四个。
323
338
  `/guidance-stats` 仍然是给终端和智能体用的文本版本(含 `--json`);面板是同一份报告的界面版本。
324
339
 
325
340
  <a id="configuration"></a>
@@ -359,6 +374,11 @@ web_guidance_search({"query": "view transition"}) → 取回之前的排序候
359
374
  **为什么结果里写着 `[experimental]`?** 指南讲的平台特性挂在 Origin Trial 后面,或者只在单一引擎里实现、仍
360
375
  不稳定 —— 见[实验性指南](#experimental-guides)。指南本身仍然是正确的方向;上线前先做特性检测。
361
376
 
377
+ **legacy 扫描器到底能做什么?** 它是正则标记扫描,不是解析器:33 条规则逐行匹配,覆盖 208 篇指南中的 33 篇
378
+ (15.9%),没有 AST、没有数据流分析、也不自动修复 —— 它只报告,然后停下。它不声称与 Chrome 自己的
379
+ `legacy-scan` 等价:那边是 4.99 MB、内置 TypeScript 编译器的引擎,这里是零依赖的扫描过程,既可能误报也可能漏报,
380
+ 所以把发现当成待核实的线索,而不是结论。
381
+
362
382
  **会有东西离开这台机器吗?** 不会。语料就在包里,检索在进程内计算,没有任何网络请求。使用计数是本地文件,
363
383
  可以用 `enabled: false` 关掉 —— 见[安全与边界](#security-and-limits)和 [ADR 0001](docs/adr/0001-local-telemetry.md)。
364
384
 
@@ -375,8 +395,9 @@ web_guidance_search({"query": "view transition"}) → 取回之前的排序候
375
395
  | 版本 | 状态 | 内容 |
376
396
  | --- | --- | --- |
377
397
  | 0.4.0 | 已发布 | 上游 v1.0.0 语料(208 篇指南)加实验性标记 |
378
- | 0.4.1 | 本次 | 文档:导航、对比表、目录地图 |
379
- | 0.5.0 | 下一步 | `legacy-scan` 作为第四个工具:从上游引擎移植的 30–35 条 regex 规则,只接受显式路径,只输出报告。设计与结论:[docs/notes/legacy-scan-feasibility.md](docs/notes/legacy-scan-feasibility.md) |
398
+ | 0.4.1 | 已发布 | 文档:导航、对比表、目录地图 |
399
+ | 0.5.0 | 已发布 | `legacy-scan` 作为第四个工具:30 条正则规则,只接受显式路径,只输出报告 —— `web_guidance_legacy_scan`,面板里多一个计数器和一个发现总数。设计与结论:[docs/notes/legacy-scan-feasibility.md](docs/notes/legacy-scan-feasibility.md) |
400
+ | 0.6.0 | 本次发布 | 再多三条规则(`:host-context()`、文本平衡、`select:invalid`),规则表增至 33 条,其中 30 条对应上游规则;另加指南哈希校验,语料更新后规则不会悄悄过期 |
380
401
 
381
402
  刻意**不**做的:内置上游那 9.16 MB 的 embedding 表,或者它那个附带 TypeScript 编译器的 4.99 MB `legacy-scan`
382
403
  引擎。两者都会破坏“零运行时依赖、不联网、不生成代码”,而换来的收益在词法索引上根本看不出来。
@@ -396,6 +417,7 @@ web_guidance_search({"query": "view transition"}) → 取回之前的排序候
396
417
  | [`audit/`](audit) | 想看 0.1.x 的安全、语料与缺陷审计原文 |
397
418
  | [`docs/adr/`](docs/adr) | 想看决策记录:本地遥测,以及之后的演变 |
398
419
  | [`docs/notes/`](docs/notes) | 想看某次发布背后的调研(上游 diff、可行性研究、计划) |
420
+ | [`docs/notes/legacy-scan-feasibility.md`](docs/notes/legacy-scan-feasibility.md) | 想看 `legacy-scan` 背后的分析:上游引擎做什么、正则扫描能匹配到什么、以及结论 |
399
421
 
400
422
  <a id="development"></a>
401
423
 
@@ -405,10 +427,11 @@ web_guidance_search({"query": "view transition"}) → 取回之前的排序候
405
427
  npm test
406
428
  ```
407
429
 
408
- `npm test` 会跑整套测试:`guidance.test.mjs` 覆盖索引、排序、取回与词干器;`plugin.test.mjs` 用模拟的 Cordis
409
- 上下文挂载插件;`hardening.test.mjs` 守住安全不变量;`report.test.mjs`、`stats.test.mjs`、`store.test.mjs` 和
410
- `telemetry.test.mjs` 覆盖计数管线;`client-locales.test.mjs` 保证面板的三份词典保持同步。在禁止子进程的沙箱
411
- 里,请直接运行文件(`node test/hardening.test.mjs`)。
430
+ `npm test` 会跑 150 个测试,分布在十二个文件里:`guidance.test.mjs` 覆盖索引、排序、取回与词干器;
431
+ `plugin.test.mjs` 用模拟的 Cordis 上下文挂载插件;`hardening.test.mjs` 守住安全不变量;`report.test.mjs`、
432
+ `stats.test.mjs`、`store.test.mjs` 和 `telemetry.test.mjs` 覆盖计数管线;`scan-core.test.mjs`、`scan-rules.test.mjs`
433
+ 和 `scan-tool.test.mjs` 覆盖扫描器的隔离、规则表与工具契约,`scan.test.mjs` 是端到端扫描;`client-locales.test.mjs`
434
+ 保证面板的三份词典保持同步。在禁止子进程的沙箱里,请直接运行文件(`node test/hardening.test.mjs`)。
412
435
 
413
436
  <a id="publishing"></a>
414
437