@7n/rules 1.43.0 → 1.44.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.
Files changed (71) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/docs/vitest.config.md +14 -19
  3. package/package.json +1 -1
  4. package/rules/abie/lib/docs/index.md +0 -2
  5. package/rules/abie/lib/http-route.mjs +1 -0
  6. package/rules/abie/lib/yaml.mjs +2 -0
  7. package/rules/ci4/marksman_config/docs/index.md +10 -0
  8. package/rules/ci4/marksman_config/main.mjs +2 -0
  9. package/rules/doc-files/docgen-prompts/docs/index.md +9 -0
  10. package/rules/doc-files/docgen-prompts/main.mjs +5 -0
  11. package/rules/hasura/internal_urls/docs/index.md +0 -2
  12. package/rules/hasura/internal_urls/docs/main.md +27 -15
  13. package/rules/hasura/internal_urls/main.mjs +1 -0
  14. package/rules/image-avif/avif_generation/docs/index.md +10 -0
  15. package/rules/image-avif/avif_generation/main.mjs +2 -0
  16. package/rules/rego/vscode_settings/docs/fix-vscode_settings.md +12 -10
  17. package/rules/rego/vscode_settings/fix-vscode_settings.mjs +5 -0
  18. package/rules/tauri/cargo_mutants_config/docs/index.md +0 -2
  19. package/rules/tauri/cargo_mutants_config/docs/main.md +38 -12
  20. package/rules/tauri/cargo_mutants_config/main.mjs +5 -1
  21. package/rules/tauri/core_test_isolation/docs/main.md +19 -16
  22. package/rules/tauri/core_test_isolation/main.mjs +3 -1
  23. package/rules/tauri/linux_deps/docs/main.md +25 -18
  24. package/rules/tauri/linux_deps/main.mjs +2 -1
  25. package/rules/tauri/release/docs/main.md +20 -12
  26. package/rules/tauri/release/main.mjs +2 -0
  27. package/rules/tauri/updater/docs/main.md +31 -14
  28. package/rules/tauri/updater/main.mjs +4 -0
  29. package/rules/test/coverage/concern.json +9 -0
  30. package/rules/test/coverage/fix-worker.mjs +111 -0
  31. package/rules/test/coverage/lib/classify/apply.mjs +67 -0
  32. package/rules/test/coverage/lib/classify/cache.mjs +77 -0
  33. package/rules/test/coverage/lib/classify/docs/apply.md +28 -0
  34. package/rules/test/coverage/lib/classify/docs/cache.md +34 -0
  35. package/rules/test/coverage/lib/classify/docs/index.md +37 -0
  36. package/rules/test/coverage/lib/classify/docs/prompt.md +30 -0
  37. package/rules/test/coverage/lib/classify/docs/verdict-schema.md +30 -0
  38. package/rules/test/coverage/lib/classify/index.mjs +140 -0
  39. package/rules/test/coverage/lib/classify/prompt.mjs +136 -0
  40. package/rules/test/coverage/lib/classify/verdict-schema.mjs +163 -0
  41. package/rules/test/coverage/lib/llm.mjs +100 -0
  42. package/rules/test/coverage/main.mjs +161 -0
  43. package/rules/test/main.json +1 -0
  44. package/rules/test/main.mdc +140 -0
  45. package/rules/test/package_json/concern.json +11 -0
  46. package/rules/test/package_json/package_json.mdc +18 -0
  47. package/rules/test/package_json/package_json.rego +25 -0
  48. package/rules/test/package_json/template/package.json.contains.json +6 -0
  49. package/rules/text/oxfmtrc/fix-oxfmtrc.mjs +5 -0
  50. package/rules/text/vscode_settings/fix-vscode_settings.mjs +5 -0
  51. package/rules/worktree/vscode_settings/docs/fix-vscode_settings.md +10 -7
  52. package/rules/worktree/vscode_settings/fix-vscode_settings.mjs +5 -0
  53. package/rules/worktree/zed_settings/fix-zed_settings.mjs +5 -0
  54. package/schemas/n-rules.json +37 -0
  55. package/scripts/lib/adr/docs/index.md +0 -2
  56. package/scripts/lib/adr/docs/normalize-pipeline.md +44 -28
  57. package/scripts/lib/adr/normalize-pipeline.mjs +11 -0
  58. package/scripts/lib/docs/inline-template-links.md +17 -8
  59. package/scripts/lib/docs/plugin-api.md +1 -1
  60. package/scripts/lib/inline-template-links.mjs +5 -0
  61. package/scripts/lib/lint-surface/docs/run-detectors.md +25 -22
  62. package/scripts/lib/lint-surface/docs/tier-sampling-experiment.md +30 -14
  63. package/scripts/lib/lint-surface/run-detectors.mjs +1 -1
  64. package/scripts/lib/lint-surface/tier-sampling-experiment.mjs +1 -0
  65. package/scripts/lib/plugin-api.mjs +46 -0
  66. package/scripts/utils/docs/walkDir.md +26 -15
  67. package/scripts/utils/docs/worktree-fingerprint.md +16 -15
  68. package/scripts/utils/walkDir.mjs +9 -5
  69. package/scripts/utils/worktree-fingerprint.mjs +5 -0
  70. package/skills/storybook/SKILL.md +4 -4
  71. package/skills/taze/js/migration-cache.mjs +1 -1
@@ -3,42 +3,58 @@ type: JS Module
3
3
  title: normalize-pipeline.mjs
4
4
  resource: npm/scripts/lib/adr/normalize-pipeline.mjs
5
5
  docgen:
6
- crc: f8bd18e2
7
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
6
+ crc: 7840afd7
7
+ model: openai-codex/gpt-5.5
8
+ tier: cloud-avg
9
+ score: 100
10
+ issues: judge-refine:kept-original,judge:inaccurate:0.99
11
+ judgeModel: openai-codex/gpt-5.4-mini
8
12
  ---
9
13
 
10
- Реалізує локально-орієнтований конвеєр для нормалізації ADR. У цій системі JavaScript виконує оркестрацію, тоді як LLM відповідає виключно на вузькі, верифіковані питання. Процес включає відбір кандидатів на основі лексичної схожості (Stage 0: retrieval), бінарне судження між записами (Stage 1: edge-judge) та класифікацію драфтів (Stage 1b: kind-judge). Система об'єднує підтверджені ребра через алгоритм Union-Find (Stage 1c: cluster) для вибору опорного елемента (anchor) та призначення операцій. Далі, LLM вилучає секції в JSON (Stage 2: gen-MADR), після чого JS збирає канон. На фінальній стадії, LLM створює прозу про змін (Stage 3: gen-merge), яку JS додає до документа з відповідним заголовком.
14
+ ## Огляд
15
+
16
+ Файл готує ADR-чернетки до застосування як `operations[]` у тому самому контракті, що й single-shot-пайплайн, сумісному з `apply-ops`: створення нового MADR, доповнення наявного ADR або пропуск. Він існує як локально-орієнтована альтернатива для малої LLM `omlx/gemma-4b`, де JS керує retrieval, кластерами, датами, MADR-каркасом і після кластеризації призначає `op`, а модель дає лише вузькі перевірювані судження та фрагменти змісту.
11
17
 
12
18
  ## Поведінка
13
19
 
14
- Поведінка
15
- tokenize токенізує назву чи слаг, повертаючи множину значущих токенів, фільтруючи стоп-слова та занадто короткі елементи.
16
- jaccard обчислює коефіцієнт Jaccard між двома множинами токенів.
17
- draftTitle витягує заголовок з тіла чернетки, використовуючи специфічні шаблони або шукаючи перший заголовок H1, не класифікований як секція MADR.
18
- isNoDecision перевіряє тіло чернетки на наявність маркерів, що свідчать про те, що рішення не було прийняте, без використання LLM.
19
- buildEdges будує пари (ребра) між чернетками та між чернетками і список чистих ADR, виходячи з лексичної схожості.
20
- validateMadr перевіряє згенерований текст MADR на відповідність формату OKF (наприклад, наявність YAML frontmatter, заголовок, статуси та секції).
21
- madrDate визначає стандартну ISO-дату для ADR, використовуючи або поле `captured` з чернетки, або префікс імені файлу.
22
- normalizeSections перетворює сирий JSON-вивід моделі на структурований об'єкт, нормалізуючи типи даних (наприклад, рядок у масив).
23
- assembleMadr збирає канонічний текст MADR, комбінуючи наданий заголовок, ISO-дату та нормалізовані секції, створюючи кінцевий markdown.
24
- genMadr викликає LLM для вилучення змісту ADR із чернетки, а потім конструює та валідує повний MADR за допомогою `assembleMadr` та `validateMadr`.
25
- normalizePipeline виконує повний конвеєр: ідентифікує кластери чернеток, визначає операції (delete, rewrite, merge-anchor тощо) та ініціює виклики LLM для генерації контенту та додатків.
20
+ `normalizePipeline` приймає батч ADR-чернеток і список уже чистих ADR, після чого веде їх через послідовність дрібних перевірюваних рішень. Глобальні рішення про зв’язки, кластери, цільові документи й фінальні operations залишаються в JS; LLM використовується лише для вузьких локальних суджень і генерації контрольованих фрагментів.
21
+
22
+ На початку потоку `draftTitle` дістає робочу назву кожної чернетки, а `isNoDecision` відсікає записи, де рішення явно не прийняте. Для решти `buildEdges` створює кандидатні зв’язки між чернетками та між чернетками і clean ADR: назви й слаги проходять через `tokenize`, а близькість кандидатів оцінює `jaccard`. Це retrieval-етап: він лише звужує простір порівнянь, а не приймає остаточне рішення про злиття.
23
+
24
+ Далі `normalizePipeline` перевіряє кандидатні зв’язки через LLM-судження «те саме рішення чи ні», консервативно об’єднує підтверджені зв’язки в кластери й призначає для кожної чернетки дію: створити новий ADR, доповнити наявний або пропустити як тривіальну/непридатну. Ізольовані чернетки додатково класифікуються як standalone або trivial, щоб не створювати ADR без достатньої змістовної ваги.
25
+
26
+ Коли потрібен новий ADR, `genMadr` перетворює одну чернетку на MADR-документ. Дату для канонічного каркаса визначає `madrDate`; модель повертає лише зміст секцій, який приводить до стабільної форми `normalizeSections`. Після цього `assembleMadr` збирає повний MADR із контрольованим заголовком, датою, статусом, назвами секцій, fallback-текстами й bullet-структурою. `validateMadr` є фінальним quality gate: невалідний документ не потрапляє в operations як готовий вміст.
27
+
28
+ Коли чернетка має доповнити наявний ADR, `normalizePipeline` генерує лише новий зміст оновлення, а службовий заголовок оновлення та дата лишаються під контролем JS через ту саму політику дат. Це зберігає однаковий формат для створення нових ADR і для merge-доповнень.
29
+
30
+ Результатом `normalizePipeline` є operations у тому самому контракті, що й single-shot-пайплайн, разом зі статистикою та діагностичним trace. Подальше застосування змін виконується спільною apply-логікою поза цим файлом; цей конвеєр лише готує перевірені наміри змін і не покладається на кешування.
26
31
 
27
32
  ## Публічний API
28
33
 
29
- Here is the rewritten list as concise bullet points:
30
-
31
- * tokenizeПеретворює назву чи слаг у набір значущих слів, розділених пробілами та дефісами.
32
- * jaccard Обчислює ступінь схожості між двома наборами слів.
33
- * draftTitle Знаходить основний заголовок чернетки, починаючи з ADR-рядка; якщо його немає, шукає перший H1, інакше бере назву файлу.
34
- * isNoDecision — Автоматично виявляє чернетки, де рішення відсутнє (наприклад, через переривання транскрипту), щоб їх не обробляти LLM.
35
- * buildEdges Створює потенційні зв'язки між елементами на основі схожості слів.
36
- * validateMadr Перевіряє якість згенерованого документа MADR автоматично.
37
- * madrDateВизначає дату документа у форматі ISO, використовуючи дані з метаданих чернетки або дату створення файлу.
38
- * normalizeSectionsСтандартизує вивід генеративної моделі в чітку структуру секцій, виправляючи невеликі помилки формату.
39
- * assembleMadrЗбирає повний документ MADR у версії 4.0.0, використовуючи фіксовану структуру та контент від інших частин.
40
- * genMadr Генерує чернетку документа MADR на основі заданих даних.
41
- * normalizePipeline Виконує послідовність усіх кроків обробки даних, повертаючи список виконаних операцій та статистику.
34
+ - tokenize Токенізує назву/слаг у множину значущих токенів (kebab + пробіли, без стоп-слів).
35
+ - jaccard — Jaccard-схожість двох множин токенів.
36
+ - draftTitleВитягує заголовок драфта. Капчер пише `## ADR <title>` він у пріоритеті
37
+ (чернетка може мати контент-заголовки раніше або взагалі не мати ADR-рядка).
38
+ Fallback-и: перший h1, що не є MADR-секцією, інакше '' (caller бере імʼя файлу).
39
+ - isNoDecision — Детермінований no-decision гейт (харднінг #1). Чернетка, де у `Decision Outcome`
40
+ рішення явно НЕ прийняте (transcript обірвався) не варта окремого ADR: gold
41
+ (sonnet) такі видаляє. Ловимо без LLM, щоб не покладатися на kind-judge малої моделі.
42
+ - buildEdgesБудує кандидати-ребра за лексичною схожістю.
43
+ - validateMadrДетермінований гейт якості згенерованого MADR.
44
+ - madrDateДетермінована ISO-дата для поля **Date:**. Пріоритет `captured` frontmatter
45
+ (перші 10 символів ISO-стемпа); fallback timestamp-префікс імені файлу
46
+ (`YYMMDD-…` `20YY-MM-DD`). Каркас MADR не повинен залежати від LLM навіть тут.
47
+ - normalizeSections — Нормалізує сирий JSON-вивід gen-моделі у строгу форму секцій. Толерантна до
48
+ дрібних відхилень малої моделі: рядок замість масиву → масив із одного елемента,
49
+ число/null → рядок/порожньо, обрізає пробіли й порожні елементи.
50
+ - assembleMadr — Детермінована збірка канонічного MADR 4.0.0 з заголовка, дати й секцій-контенту.
51
+ Увесь каркас (Status, назви секцій, шаблон "Chosen option…", fallback-фрази,
52
+ bullets) — тут, не в моделі. Заголовок і дата — JS-власність (draftTitle/captured),
53
+ модель їх не торкається.
54
+ - genMadr — Stage 2: перетворює одну чернетку на валідний MADR-документ. Модель лише
55
+ витягує зміст секцій як JSON; каркас збирає JS, результат проганяється через
56
+ валідатор із ретраями каскаду. При невдачі повертає `valid: false` без вмісту.
57
+ - normalizePipeline — Головний конвеєр. Повертає operations[] (контракт single-shot) + stats.
42
58
 
43
59
  ## Гарантії поведінки
44
60
 
@@ -459,6 +459,17 @@ export function assembleMadr({ title, date, sections: s }) {
459
459
  ].join('\n')
460
460
  }
461
461
 
462
+ /**
463
+ * Stage 2: перетворює одну чернетку на валідний MADR-документ. Модель лише
464
+ * витягує зміст секцій як JSON; каркас збирає JS, результат проганяється через
465
+ * валідатор із ретраями каскаду. При невдачі повертає `valid: false` без вмісту.
466
+ * @param {string} title заголовок рішення
467
+ * @param {string} body тіло чернетки (обрізається до безпечного розміру)
468
+ * @param {string} captured момент фіксації чернетки (для дати MADR)
469
+ * @param {{allowCloud: boolean, stats: object}} cfg конфіг каскаду й лічильники
470
+ * @param {string} [file] шлях чернетки — fallback-джерело дати
471
+ * @returns {Promise<{content: string | null, slug: string, valid: boolean, error?: string}>} зібраний документ або позначка невдачі
472
+ */
462
473
  export async function genMadr(title, body, captured, cfg, file = '') {
463
474
  const date = madrDate(captured, file)
464
475
  const slug = slugify(title)
@@ -3,23 +3,32 @@ type: JS Module
3
3
  title: inline-template-links.mjs
4
4
  resource: npm/scripts/lib/inline-template-links.mjs
5
5
  docgen:
6
- crc: 35230d2d
7
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
6
+ crc: dde8124c
7
+ model: openai-codex/gpt-5.4-mini
8
+ tier: cloud-min
8
9
  score: 100
10
+ issues: judge-refine:kept-original,judge:inaccurate:0.96
11
+ judgeModel: openai-codex/gpt-5.4-mini
9
12
  ---
10
13
 
11
- Модуль збагачує текстовий контент, використовуючи конфігурації з package.json.snippet.json та package.json. Функція inlineTemplateLinks замінює текстові посилання на шаблони вбудованими блоками, якщо відповідні файли знаходяться у директорії правил. Функція appendDiscoveredMdcFiles доповнює текст вмістом усіх знайдених файлів `.mdc` з піддиректорій `js/` та `policy/` у директорії правил.
14
+ ## Огляд
15
+
16
+ Перетворює markdown-лінки на файли з `template/` у fenced-блоки з фактичним вмістом цих файлів і додає знайдені `.mdc`-правила, щоб зібраний `.mdc` лишався самодостатнім для подальшого використання без відносних шляхів. Поведінку реалізують `inlineTemplateLinks` і `appendDiscoveredMdcFiles`.
12
17
 
13
18
  ## Поведінка
14
19
 
15
- inlineTemplateLinks замінює посилання на шаблони в тексті на вбудовані блоки з вмістом файлу, якщо ці файли існують у вказаній директорії правил.
16
- appendDiscoveredMdcFiles додає до кінця тексту вміст усіх знайдених файлів `.mdc` з піддиректорій `js/` та `policy/` у директорії правил.
20
+ inlineTemplateLinks спочатку підбирає лише ті markdown-лінки, що ведуть у template-піддерево, і замінює їх самодостатнім вбудованим фрагментом із фактичним вмістом файлу; так правило перестає залежати від відносних посилань. Для вкладених прикладів і слот-файлів відновлюється читабельна назва цілі, зокрема для `package.json.snippet.json` і `package.json`, а формат fenced-блока обирається за розширенням вмісту. Якщо ціль не знайдено, обробка зупиняється з помилкою, щоб згенерований `.mdc` не містив битих посилань.
21
+
22
+ appendDiscoveredMdcFiles далі доповнює вже оброблений текст усіма знайденими `.mdc`-файлами з піддиректорій, які позначені через `concern.json`; це дає змогу зібрати повний пакет правил без ручного перелічення допоміжних файлів. Порядок стабільний: спочатку директори сортуються, потім файли всередині кожного каталогу, а результати додаються в кінець як один суцільний блок. Якщо таких директорій немає, початковий текст лишається без змін.
17
23
 
18
24
  ## Публічний API
19
25
 
20
- inlineTemplateLinks — Замінює посилання на шаблони в Markdown на вбудовані блоки, якщо шлях містить `/template/`. Помилка виникає, якщо цільовий файл посилання відсутній.
21
- appendDiscoveredMdcFiles Додає всі знайдені файли `.mdc` з папок `js/` та `policy/<concern>/`. Файли з `js/` йдуть першими, а потім файли з підпапок `policy/<concern>/` (у алфавітному порядку за `concern`, а потім за назвою файлу).
26
+ - inlineTemplateLinks — Finds markdown links whose path contains /template/ and replaces them with
27
+ inline fenced blocks. Reads file from join(ruleDir, rel-path).
28
+ Throws Error if a matched link target doesn't exist (fail loud — user must know).
29
+ - appendDiscoveredMdcFiles — Appends all *.mdc files from concern subdirectories (those with concern.json).
30
+ Concerns ordered alphabetically; files within each concern ordered alphabetically.
22
31
 
23
32
  ## Гарантії поведінки
24
33
 
25
- - Read-only: не виконує операцій запису (ФС/БД).
34
+ - Власних операцій запису (ФС/БД) у файлі немає; виклики імпортованих модулів можуть писати.
@@ -3,7 +3,7 @@ type: JS Module
3
3
  title: plugin-api.mjs
4
4
  resource: npm/scripts/lib/plugin-api.mjs
5
5
  docgen:
6
- crc: c614a880
6
+ crc: 72b55c0e
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
9
  issues: judge:inaccurate:0.98
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Інлайн template-посилань у .mdc-правилах: markdown-лінки на файли з тек
3
+ * template/ замінюються fenced-блоками з фактичним вмістом файлу, щоб
4
+ * згенероване правило було самодостатнім без відносних шляхів.
5
+ */
1
6
  import { existsSync } from 'node:fs'
2
7
  import { readFile } from 'node:fs/promises'
3
8
  import { basename, extname, join } from 'node:path'
@@ -3,42 +3,45 @@ type: JS Module
3
3
  title: run-detectors.mjs
4
4
  resource: npm/scripts/lib/lint-surface/run-detectors.mjs
5
5
  docgen:
6
- crc: a0f45fc3
7
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
6
+ crc: e9224e37
7
+ model: openai-codex/gpt-5.4-mini
8
+ tier: cloud-min
8
9
  score: 100
9
- issues: judge:inaccurate:0.98
10
+ issues: judge-refine:kept-original,judge:inaccurate:0.99
10
11
  judgeModel: openai-codex/gpt-5.4-mini
11
12
  ---
12
13
 
13
14
  ## Огляд
14
15
 
15
- Цей файл є детектором для уніфікованої поверхні лінту, який сканує код без внесення змін. Він виконує процес вибору області перевірки (`scope-selection`), збирає всі виявлені порушення (`normalized violations`) для кожного аспекту (`concern`) відповідно до конфігурації `.n-rules.json`. Цей компонент працює у режимі `Detect-only`, тобто він не вносить жодних мутацій і не використовує LLM. Порушення передаються до пайплайну виправлення (Fix-pipeline) для подальшої обробки.
16
+ Файл формує єдину поверхню для `n-rules lint --no-fix`: від discovery правил і вибору меж запуску до виконання `lint` для кожного concern та збору нормалізованих violations. Його результатом є список порушень, який використовує fix-pipeline для подальшої обробки, тоді як сам detect не змінює дерево файлів.
16
17
 
17
18
  ## Поведінка
18
19
 
19
- Поведінка
20
- DEFAULT_RULES_DIR визначає шлях до стандартної директорії з правилами.
21
- buildDetectPlan створює впорядкований план прогону лінтера, збираючи всі відповідні концерни та визначаючи область їх сканування.
22
- detectAll виконує прохід лінтера у режим детекції, збираючи всі виявлені порушення та повертаючи код виходу.
20
+ DEFAULT_RULES_DIR задає базовий корінь правил, від якого стартує discovery, коли споживач не передав власні каталоги.
23
21
 
24
- **Внутрішній паралелізм (ADR 260716-1354-внутрішній-паралелізм-lint-оркестратора).** `detectAll` читає `N_RULES_LINT_CONCURRENCY` (дефолт `1` production-паралелізм ще не пройшов benchmark-gates ADR): за замовчуванням план виконується повністю послідовно (`detectPlanSequentially`), спостережувано ідентично до-ADR поведінці. При `concurrency > 1` план ділиться на два лейни через `blocking-inventory.mjs` (`isSerialLane`) і виконується через `scheduler.mjs` (`runPlanConcurrently`, `detectPlanConcurrently`): parallel lane — доведені non-blocking concern-и, bounded pool до `concurrency`; serial lane — решта, строго послідовно. Перший `DetectorError` (будь-який лейн) зупиняє нові старти, `AbortController` сигналізує вже запущеним async-детекторам (`ctx.signal`), а вже завершені concern-и лишаються в результаті `exitCode 2` пріоритетний над частковими violations. Фінальний масив violations завжди стабільно сортується за `(ruleId, concernId, file, data.line, reason)` — незалежно від порядку завершення (`sortViolations`), навіть при `concurrency=1`.
22
+ buildDetectPlan спочатку визначає набір rules-каталогів, потім відбирає лише доступні concern-и з урахуванням capability, далі будує план виконання за режимом прогону: scoped, delta, full або repo-wide. Сам план фіксує, які concern-и запускаються whole-repo, а які лише по перетину з файлами, щоб detect і fix-pipeline працювали з однаковою картиною.
25
23
 
26
- ## Публічний API
27
-
28
- * DEFAULT_RULES_DIR — Містить набір стандартних правил для перевірок.
29
- * buildDetectPlan — Створює план виконання перевірок, охоплюючи визначені області.
30
- * detectAll — Виконує повний прохід пошуку проблем і повертає всі виявлені порушення та код виходу.
31
- * loadEnabledLintRules — Discovery-фасад для споживачів поза detect/fix-конвеєром (`ci plan`): concerns за rule-id + set активних правил.
32
- * computeActiveDomains — Активність доменів для файлового набору (лише per-file concerns) — єдине джерело правди для `ci plan`: «plan сказав true» ⇔ «lint щось запустить».
24
+ loadEnabledLintRules використовує той самий discovery-ланцюжок, але повертає не план, а повну мапу concern-и за rule-id разом із множиною активних правил. Це потрібно зовнішнім споживачам, які мають знати, що реально доступно для прогону, не запускаючи сам detector-цикл.
33
25
 
34
- **Осі плану (сервіс-канон):** scoped + explicit files (`lint js --path <dir>`) лише per-file concerns названих правил × перетин; `pathMode` (дефолтний `--path`) → full-scope concerns виключені з delta-плану; `repoWide` (`--repo-wide`) ЛИШЕ full-scope concerns enabled-правил, whole-repo (окремий CI-workflow, не гейтить деплой).
26
+ computeActiveDomains дає скорочений зріз цієї ж моделі: для заданого набору файлів показує, які rule-id справді активуються хоча б одним per-file concern-ом. Full-scope перевірки тут навмисно не враховуються, бо їхня зона відповідає repo-wide прогону.
35
27
 
36
- ## Гарантії поведінки
28
+ detectAll бере готовий план, виконує його, збирає normalized violations і повертає ще й derived exitCode. Увесь шлях read-only: нічого не записує в дерево, не змінює конфіг і не покладається на мутації поза межами збирання результатів. Фільтрація та видимість правил спираються на .n-rules.json, тож саме він визначає, які rules каталоги й concern-и вважаються активними.
37
29
 
38
- - Read-only: не виконує операцій запису (ФС/БД).
30
+ ## Публічний API
39
31
 
40
- **Multi-dir (плагіни):** `effectiveRulesDirs` додає rules-каталоги плагінів з `.n-rules.json` (hot-path: без install, quiet); `readLintConcernsByRuleMulti` зливає концерни за іменем (перший власник виграє) — плагін може додавати концерни до правила ядра (mixin).
32
+ - DEFAULT_RULES_DIR Цей файл: npm/scripts/lib/lint-surface/run-detectors.mjs PACKAGE_ROOT = npm (4 dirname угору).
33
+ - buildDetectPlan — Будує план прогону для заданих опцій (discovery + scope-table).
34
+ Спільне джерело для detect-only і fix-pipeline.
35
+ - loadEnabledLintRules — Discovery-фасад для споживачів поза detect/fix-конвеєром (`ci plan`):
36
+ concerns за rule-id (ядро + плагіни, capability-фільтр) і set активних правил.
37
+ - computeActiveDomains — Активність доменів (rule-id) для заданого файлового набору — єдине джерело
38
+ правди для `ci plan`: домен «активний», якщо хоч один його **per-file**
39
+ concern тригериться на цих файлах (та сама таблиця planConcernForDelta, що
40
+ й `lint <domain> --path` → «plan сказав true» ⇔ «lint щось запустить»).
41
+ Правила без жодного per-file concern не потрапляють у результат (їхні
42
+ full-scope перевірки — справа `--repo-wide`).
43
+ - detectAll — Запускає detect-only прохід. Повертає всі violations і похідний exitCode.
41
44
 
42
- **Capability-гейт:** `filterByCapabilities` відкидає концерни з незадоволеним `requires.capability` (capabilities надають встановлені плагіни через маніфест `n-rules.capabilities`; явний `opts.capabilities` у тестах перекриває резолв).
45
+ ## Гарантії поведінки
43
46
 
44
- **Warning про rule-id без concern-ів:** якщо rule-id з `.n-rules.json#rules` не має жодного concern-а серед усіх `rulesDirs` (ядро + плагіни) — `console.error` попереджає про можливий дрейф конфігу (типово: правило переїхало в плагін, якого консюмер не підключив у `plugins[]`). Rule-id з існуючим каталогом, але без `concern.json` (документаційні правила на кшталт `feedback`), warning не тригерить.
47
+ - Власних операцій запису (ФС/БД) у файлі немає; виклики імпортованих модулів можуть писати.
@@ -3,32 +3,48 @@ type: JS Module
3
3
  title: tier-sampling-experiment.mjs
4
4
  resource: npm/scripts/lib/lint-surface/tier-sampling-experiment.mjs
5
5
  docgen:
6
- crc: f6a46f92
7
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
6
+ crc: 959712b3
7
+ model: openai-codex/gpt-5.5
8
+ tier: cloud-avg
8
9
  score: 100
9
- issues: judge:inaccurate:0.98
10
+ issues: judge:error
10
11
  judgeModel: openai-codex/gpt-5.4-mini
11
12
  ---
12
13
 
13
14
  ## Огляд
14
15
 
15
- Файл слугує експериментальною обгорткою для перевірки процесу виправлення (lint fix ladder). Він моделює ізольовані потенційні рішення для певного рівня, відкочуючи робоче дерево до початкового стану S1 перед кожною спробою. Мета модуля ізольовано протестувати кандидати, які пройшли автоматичне виявлення чистоти, щоб оцінити їхню цінність через механізм зворотного зв'язку.
16
+ Експериментальний harness моделює isolated sampling для окремого tier-а lint fix ladder: будує experiment-only rung-и, проганяє sampling-кандидатів із відкотом до S1 і лишає лише candidate, що пройшов canonical detect. Файл існує, щоб перевіряти sampling/consensus поза production `runFixPipeline`, залишаючи judge/consensus тільки джерелом feedback, а не рішенням про patch.
17
+
18
+ Публічні точки входу: `EXPERIMENT_TIER_ORDER`, `buildExperimentLadder`, `samplingProfilesForTier`, `chooseCleanCandidate`, `runTierSamplingExperiment`.
19
+
20
+ Модуль працює fail-safe: перехоплює помилки й не кидає винятків назовні.
16
21
 
17
22
  ## Поведінка
18
23
 
19
- EXPERIMENT_TIER_ORDER визначає фіксований порядок експериментальних рівнів.
20
- buildExperimentLadder конструює список експериментальних етапів на основі наданих моделей для кожного tier.
21
- samplingProfilesForTier генерує список можливих профілів відбору для певного tier.
22
- chooseCleanCandidate вибирає найкращий кандидат серед усіх спроб, які пройшли перевірку на чистоту.
23
- runTierSamplingExperiment виконує послідовність випробувань (sampling) для заданого tier, тестуючи кандидатів, обираючи найкращого та застосовуючи його патч для фінальної перевірки.
24
+ `EXPERIMENT_TIER_ORDER` задає порядок експериментальних tier-ів, за яким формується ladder для isolated sampling поверх lint fix pipeline.
25
+
26
+ `buildExperimentLadder` створює окремий experiment-only ladder із підтримкою `cloud-max`, не змінюючи production-послідовність `runFixPipeline`. Дані про моделі й timeouts перетворюються на набір rung-ів, які далі використовуються як сценарії окремих випробувань.
27
+
28
+ `samplingProfilesForTier` визначає набір sampling-кандидатів для конкретного tier-а. Якщо для tier-а передані перевизначення, вони стають джерелом профілів; інакше використовується стандартний набір для цього рівня.
29
+
30
+ `runTierSamplingExperiment` бере violations, базовий контекст, rung і sampling-кандидатів, після чого послідовно тестує кожного кандидата в ізольованому стані S1. Для кожної спроби робоче дерево відкочується до початкового snapshot, запускається worker, фіксуються змінені файли, виконується canonical detect і записується результат спроби разом із telemetry та latency.
31
+
32
+ Після всіх спроб `chooseCleanCandidate` обирає лише чистий candidate: пріоритет має менша кількість змінених файлів, менший patch і коротша latency. Якщо чистого кандидата немає, експеримент завершується без застосування patch до робочого дерева.
33
+
34
+ Якщо чистий candidate знайдено, `runTierSamplingExperiment` застосовує саме його зміни як фінальний результат tier sampling. Judge/consensus не приймає рішення про застосування patch: його відповідь повертається тільки як feedback поруч із attempts, selected candidate, фінальними violations і clean-статусом.
35
+
36
+ Модуль працює fail-safe: помилки окремих кандидатів або допоміжних етапів перетворюються на результати спроб і не викидаються назовні. Кешування між запусками або кандидатами не використовується.
24
37
 
25
38
  ## Публічний API
26
39
 
27
- * EXPERIMENT_TIER_ORDER — Визначає пріоритетність випробувань (tiers).
28
- * buildExperimentLadder — Створює ієрархію для експериментів, не впливаючи на конфігурацію продакшену.
29
- * samplingProfilesForTier — Повідомляє про доступні варіанти збору зразків для конкретного рівня випробувань.
30
- * chooseCleanCandidate — Вибирає оптимальний варіант з усіх тестових спроб, базуючись на мінімальній кількості змін у файлах, розмірі патчу та латентності.
31
- * runTierSamplingExperiment Проводить комплекс випробувань для одного рівня: для кожного варіанту повертається до початкового стану, запускається робочий процес, оцінюється якість, а при виборі — застосовується відповідний виправлення.
40
+ - EXPERIMENT_TIER_ORDER — Порядок tier-ів експериментального ladder — від локальної моделі до найсильнішої хмарної.
41
+ - buildExperimentLadder — Будує experiment-only ladder із `cloud-max`. Production ladder це не змінює.
42
+ - samplingProfilesForTier — Повертає можливі sampling profiles для заданого tier-а.
43
+ - chooseCleanCandidate — Дефолтний вибір найкращого (clean) кандидата серед усіх спроб.
44
+ Вибір відбувається за критеріями: менша кількість змінених файлів, менший розмір patch, менша латентність.
45
+ - runTierSamplingExperiment — Виконує послідовність випробувань (sampling) для одного tier.
46
+ Для кожного кандидата виконується rollback до S1, запускається worker,
47
+ оцінюється чистота, і якщо кандидат обраний, застосовується його patch.
32
48
 
33
49
  ## Гарантії поведінки
34
50
 
@@ -25,7 +25,7 @@ import { renderViolations, renderDiagnostics } from './render.mjs'
25
25
  import { createProgressReporter } from './progress.mjs'
26
26
  import { runPlanConcurrently } from './scheduler.mjs'
27
27
 
28
- // Цей файл: npm/scripts/lib/lint-surface/run-detectors.mjs → PACKAGE_ROOT = npm (4 dirname угору).
28
+ /** Цей файл: npm/scripts/lib/lint-surface/run-detectors.mjs → PACKAGE_ROOT = npm (4 dirname угору). */
29
29
  export const DEFAULT_RULES_DIR = join(dirname(dirname(dirname(dirname(fileURLToPath(import.meta.url))))), 'rules')
30
30
 
31
31
  /**
@@ -10,6 +10,7 @@ import { dirname } from 'node:path'
10
10
 
11
11
  import { createSnapshot } from './snapshot.mjs'
12
12
 
13
+ /** Порядок tier-ів експериментального ladder — від локальної моделі до найсильнішої хмарної. */
13
14
  export const EXPERIMENT_TIER_ORDER = Object.freeze(['local-min', 'cloud-min', 'cloud-avg', 'cloud-max'])
14
15
 
15
16
  const DEFAULT_LOCAL_TIMEOUT_MS = 300_000
@@ -78,6 +78,52 @@ export const PLUGIN_API_VERSION = 1
78
78
  const REQUIRED_PROVIDER_FUNCTIONS = ['detect', 'available', 'backup', 'bump', 'diff', 'promptFor', 'cleanup']
79
79
  const REQUIRED_PROVIDER_STRINGS = ['id', 'title', 'manifestNoun', 'skillSection']
80
80
 
81
+ /**
82
+ * @typedef {object} CoverageRow
83
+ * Агрегований вимір однієї області (`JS`, `Vue (Storybook)`, `Rust`, …).
84
+ * @property {string} area назва рядка звіту
85
+ * @property {{lines:{covered:number,total:number}, functions:{covered:number,total:number}}} coverage line/function coverage
86
+ * @property {{caught:number, total:number}} mutation вбиті/всі мутанти (0/0 — мутаційне тестування не вимірювалось)
87
+ * @property {Array<{file:string, mutants:Array<object>, exampleTest?:object|null, recommendationText?:string|null}>} survived вцілілі мутанти по файлах (шляхи relative до cwd)
88
+ */
89
+
90
+ /**
91
+ * @typedef {object} CoverageProvider
92
+ * Порт мовної екосистеми для концерну `coverage` правила `test` (spec
93
+ * 2026-07-22 absorb-7n-test). Ядро викликає: `detect` → повний вимір
94
+ * `collect` (full/`lint test`) АБО делта-вимір `collectPerFile` (делта-lint,
95
+ * без мутаційного тестування). Реєстрація — `contributes.handlers.coverage`
96
+ * у маніфесті плагіна, default-експорт handler-модуля.
97
+ * @property {string} id стабільний ідентифікатор екосистеми (напр. `js`)
98
+ * @property {string} title заголовок для звітів/повідомлень
99
+ * @property {(cwd: string) => Promise<boolean>} detect чи застосовний вимір у проєкті (false → тихий skip виміру)
100
+ * @property {(cwd: string, opts?: {changedFiles?: string[], base?: string|null, runner?: object}) => Promise<CoverageRow[]>} collect повний вимір: coverage + мутаційне тестування по всіх workspaces
101
+ * @property {(cwd: string, opts: {files: string[], runner?: object}) => Promise<Array<{file:string, pct:number, linesFound:number, linesCovered:number, reason?:string}>>} collectPerFile легкий делта-вимір per-file line coverage змінених файлів (без мутаційного тестування)
102
+ */
103
+
104
+ const REQUIRED_COVERAGE_FUNCTIONS = ['detect', 'collect', 'collectPerFile']
105
+ const REQUIRED_COVERAGE_STRINGS = ['id', 'title']
106
+
107
+ /**
108
+ * Валідує форму coverage-провайдера з модуля плагіна.
109
+ * @param {unknown} candidate default-експорт handler-модуля плагіна
110
+ * @param {string} source ім'я плагіна/шлях модуля для повідомлення
111
+ * @returns {CoverageProvider} той самий обʼєкт, якщо валідний
112
+ */
113
+ export function assertCoverageProvider(candidate, source) {
114
+ if (!candidate || typeof candidate !== 'object') {
115
+ throw new TypeError(`plugin-api: ${source} — default-експорт не є обʼєктом CoverageProvider`)
116
+ }
117
+ const missing = [
118
+ ...REQUIRED_COVERAGE_STRINGS.filter(k => typeof candidate[k] !== 'string' || candidate[k] === ''),
119
+ ...REQUIRED_COVERAGE_FUNCTIONS.filter(k => typeof candidate[k] !== 'function')
120
+ ]
121
+ if (missing.length > 0) {
122
+ throw new TypeError(`plugin-api: ${source} — CoverageProvider без обовʼязкових полів: ${missing.join(', ')}`)
123
+ }
124
+ return /** @type {CoverageProvider} */ (candidate)
125
+ }
126
+
81
127
  /**
82
128
  * Валідує форму провайдера з модуля плагіна — зрозуміла помилка замість
83
129
  * «undefined is not a function» глибоко в оркестраторі.
@@ -3,31 +3,42 @@ type: JS Module
3
3
  title: walkDir.mjs
4
4
  resource: npm/scripts/utils/walkDir.mjs
5
5
  docgen:
6
- crc: 6f26e08f
7
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
6
+ crc: 1bbc24bc
7
+ model: openai-codex/gpt-5.5
8
+ tier: cloud-avg
8
9
  score: 100
10
+ judgeModel: openai-codex/gpt-5.4-mini
9
11
  ---
10
12
 
11
- Публічна функція `walkDir` здійснює рекурсивний пошук файлів у вказаному каталозі. Вона обходить файлову систему, ігноруючи каталоги `.git`, `node_modules` та worktree-чекаути (`.worktrees/`, `.claude/worktrees/` — повні копії репо від сесійних git-worktree Claude/агентів). Знаходячи кожен файл, вона передає його повний шлях у колбек для подальшої обробки. Компонент є read-only, тобто не виконує записів у файлову систему чи бази даних. При роботі з файловою системою він перехоплює помилки, забезпечуючи безпечну роботу без викидання винятків.
13
+ ## Огляд
14
+
15
+ Файл описує правила пропуску службових і агентських шляхів під час читання репозиторію. `WORKTREE_CHECKOUT_GLOBS` задає шаблони агентських worktree-копій, `ALWAYS_IGNORE` фіксує завжди ігноровані `.git` і `node_modules`.
16
+
17
+ `isWorktreeCheckoutPath` перевіряє вже відомий відносний шлях на належність до агентського worktree. `walkDir` виконує fail-safe обхід файлового дерева: перехоплює помилки читання і не передає винятки назовні.
12
18
 
13
19
  ## Поведінка
14
20
 
15
- 1. Викликається функція walkDir для початку рекурсивного обходу каталогу.
16
- 2. Система розшифровує вхідний шлях до кореня обходу.
17
- 3. Система формує додаткові правила ігнорування на основі наданих шляхів.
18
- 4. Система завжди ігнорує каталоги .git, node_modules і worktree-чекаути (.worktrees/, .claude/worktrees/) на будь-якій глибині.
19
- 5. Система виконує пошук усіх файлів у каталозі, застосовуючи правила ігнорування, включаючи ті, що визначені в .gitignore.
20
- 6. У разі виникнення помилки під час пошуку, процес обходу припиняється без генерації винятку.
21
- 7. Для кожного знайденого файлу система викликає наданий колбек, передаючи йому повний абсолютний шлях до цього файлу.
21
+ `WORKTREE_CHECKOUT_GLOBS` задає спільне правило безпеки для агентських worktree-копій, щоб обхід не сприймав дублікати репозиторію як робочий код.
22
+
23
+ `ALWAYS_IGNORE` розширює це правило постійними пропусками `.git` і `node_modules`; ці винятки застосовуються під час повного обходу незалежно від налаштувань репозиторію.
24
+
25
+ `walkDir` приймає корінь обходу, додає до спільних правил додаткові локальні винятки, читає файлове дерево з урахуванням `.gitignore` і передає знайдені файли далі як абсолютні шляхи. Якщо обхід неможливий, він завершується без помилки назовні, щоб перевірки залишались fail-safe.
26
+
27
+ `isWorktreeCheckoutPath` використовує те саме смислове правило пропуску worktree-чекаутів для уже наявних відносних шляхів, які не проходять через `walkDir`, наприклад зі списків git.
22
28
 
23
29
  ## Публічний API
24
30
 
25
- - walkDirрекурсивно переглядає файли та папки, ігноруючи ті, що вказані у файлі `.gitignore`.
26
- - isWorktreeCheckoutPath предикат для відносного posix-шляху: чи лежить він усередині worktree-чекаута (`.worktrees/` або `.claude/worktrees/`). Для фільтрації git-списків, що не проходять через walkDir (наприклад, delta-список змінених файлів).
27
- - ALWAYS_IGNORE, WORKTREE_CHECKOUT_GLOBS експортовані glob-константи безумовних пропусків обходу.
31
+ - WORKTREE_CHECKOUT_GLOBSСесійні git-worktree чекаути (Claude/агенти): повні копії репо, не робочий код.
32
+ Споживацькі репо часто не мають цих шляхів у .gitignore, тож без safety net
33
+ lint-обхід бачив би дубль усього дерева.
34
+ - ALWAYS_IGNORE — .git ніколи не потрапляє в .gitignore — пропускаємо завжди.
35
+ node_modules — safety net: проєкт може не мати .gitignore або запускатись поза git-репо.
36
+ - isWorktreeCheckoutPath — Чи лежить відносний posix-шлях усередині worktree-чекаута (`.worktrees/` або
37
+ `.claude/worktrees/`). Для фільтрації git-списків, що не проходять через walkDir.
38
+ - walkDir — Рекурсивно обходить каталог, поважаючи .gitignore (включно з вкладеними).
28
39
 
29
40
  ## Гарантії поведінки
30
41
 
31
- - Read-only: не виконує операцій запису (ФС/БД).
42
+ - Власних операцій запису (ФС/БД) у файлі немає; виклики імпортованих модулів можуть писати.
32
43
  - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
33
- - Свідомо пропускає шляхи: `.git`, `node_modules`, `.worktrees/`, `.claude/worktrees/`.
44
+ - Свідомо пропускає шляхи: `.git`, `node_modules`.
@@ -3,29 +3,30 @@ type: JS Module
3
3
  title: worktree-fingerprint.mjs
4
4
  resource: npm/scripts/utils/worktree-fingerprint.mjs
5
5
  docgen:
6
- crc: df297382
6
+ crc: 38831a93
7
+ model: openai-codex/gpt-5.4-mini
8
+ tier: cloud-min
9
+ score: 100
10
+ judgeModel: openai-codex/gpt-5.4-mini
7
11
  ---
8
12
 
9
- Файл надає унікальний відбиток робочої директорії. Цей відбиток використовується для ідентифікації та порівняння робочих директорій, забезпечуючи їх унікальність у системі. Він слугує базовим інструментом для відстеження та управління версіями.
13
+ ## Огляд
14
+
15
+ `worktreeFingerprint` формує стабільний fingerprint git-робочого дерева з `HEAD`, diff і `untracked`-файлів. Це потрібно, щоб розпізнавати той самий стан дерева й дедуплікувати повторні `lint --full` прогони без змін.
10
16
 
11
17
  ## Поведінка
12
18
 
13
- 1. Отримує поточний SHA256 хеш коміту, який знаходиться в гілці HEAD.
14
- 2. Отримує текст зміни між комітом HEAD та робочим каталогом.
15
- 3. Отримує список не відстежених файлів у робочому каталозі, не включаючи стандартні файли, що ігноруються.
16
- 4. Для кожного не відстеженого файлу отримує його SHA256 хеш.
17
- 5. Об'єднує SHA256 хеш коміту, текст зміни та пари "файл:хеш" не відстежених файлів, розділяючи їх новими рядками.
18
- 6. Обчислює SHA256 хеш отриманого текстового рядка.
19
- 7. Повертає отриманий SHA256 хеш у форматі 64-значної шестнадцяткової строки.
20
- 8. Якщо виникає помилка під час виконання будь-якої з операцій, повертає `null`.
19
+ 1. worktreeFingerprint визначає, чи поточне git-робоче дерево вже має унікальний стан для повторного повного lint-прогону.
20
+ 2. Якщо дерево не належить git-репо або перевірку неможливо виконати, повертає `null`.
21
+ 3. Інакше бере базовий стан з `HEAD`, додає різницю від `HEAD` і враховує всі untracked-файли, щоб зміна вмісту або набору нових файлів впливала на результат.
22
+ 4. Для untracked-файлів формує впорядковану сукупність «шлях + вміст», щоб однаковий набір файлів давав той самий fingerprint незалежно від порядку їх виявлення.
23
+ 5. На основі цього зводить весь стан дерева в один SHA-256 fingerprint у вигляді hex-рядка.
24
+ 6. Використовується як стабільний маркер незміненого дерева для відсікання повторних повних lint-прогонів.
21
25
 
22
26
  ## Публічний API
23
27
 
24
- worktreeFingerprint — генерує унікальний відбиток поточного стану репозиторію Git.
28
+ - worktreeFingerprint — Fingerprint поточного стану git-робочого дерева.
25
29
 
26
30
  ## Гарантії поведінки
27
31
 
28
- - Функція повертає `true`, якщо відбиток дерева робіт успішно обчислено.
29
- - Функція повертає `null`, якщо обчислення відбитка дерева робіт не вдалося.
30
- - Функція не викликає винятків.
31
- - Функція не використовує кеш.
32
+ - (специфічних машинно-виведених гарантій немає)
@@ -2,13 +2,17 @@
2
2
  import { join, relative, resolve, sep } from 'node:path'
3
3
  import { globby } from 'globby'
4
4
 
5
- // Сесійні git-worktree чекаути (Claude/агенти): повні копії репо, не робочий код.
6
- // Споживацькі репо часто не мають цих шляхів у .gitignore, тож без safety net
7
- // lint-обхід бачив би дубль усього дерева.
5
+ /**
6
+ * Сесійні git-worktree чекаути (Claude/агенти): повні копії репо, не робочий код.
7
+ * Споживацькі репо часто не мають цих шляхів у .gitignore, тож без safety net
8
+ * lint-обхід бачив би дубль усього дерева.
9
+ */
8
10
  export const WORKTREE_CHECKOUT_GLOBS = ['**/.worktrees/**', '**/.claude/worktrees/**']
9
11
 
10
- // .git ніколи не потрапляє в .gitignore — пропускаємо завжди.
11
- // node_modules safety net: проєкт може не мати .gitignore або запускатись поза git-репо.
12
+ /**
13
+ * .git ніколи не потрапляє в .gitignore пропускаємо завжди.
14
+ * node_modules — safety net: проєкт може не мати .gitignore або запускатись поза git-репо.
15
+ */
12
16
  export const ALWAYS_IGNORE = ['.git/**', 'node_modules/**', ...WORKTREE_CHECKOUT_GLOBS]
13
17
 
14
18
  const WORKTREE_CHECKOUT_RE = /(?:^|\/)(?:\.worktrees|\.claude\/worktrees)\//u
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Fingerprint git-робочого дерева (HEAD + diff + untracked-файли) —
3
+ * використовується для дедуплікації повторних lint --full прогонів на
4
+ * незміненому дереві.
5
+ */
1
6
  import { spawnSync } from 'node:child_process'
2
7
  import { createHash } from 'node:crypto'
3
8