@nitra/cursor 5.3.4 → 6.0.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 (173) hide show
  1. package/.claude-template/settings.template.json +2 -2
  2. package/.pi-template/extensions/n-cursor-adr/docs/index.md +13 -24
  3. package/CHANGELOG.md +21 -0
  4. package/bin/n-cursor.js +47 -24
  5. package/lib/docs/models.md +29 -18
  6. package/lib/docs/omlx-trace.md +51 -0
  7. package/lib/docs/omlx.md +31 -15
  8. package/lib/omlx.mjs +2 -5
  9. package/package.json +1 -1
  10. package/rules/abie/docs/fix.md +17 -11
  11. package/rules/adr/docs/fix.md +25 -140
  12. package/rules/bun/docs/fix.md +18 -151
  13. package/rules/capacitor/docs/fix.md +16 -13
  14. package/rules/capacitor/js/docs/platforms.md +31 -43
  15. package/rules/changelog/docs/fix.md +25 -169
  16. package/rules/ci4/docs/fix.md +11 -14
  17. package/rules/doc-files/doc-files.mdc +60 -0
  18. package/rules/doc-files/docs/fix.md +31 -0
  19. package/rules/doc-files/fix.mjs +19 -0
  20. package/{skills → rules}/doc-files/js/docgen-extract.mjs +42 -19
  21. package/{skills → rules}/doc-files/js/docgen-files-batch.mjs +18 -5
  22. package/{skills → rules}/doc-files/js/docgen-gen.mjs +46 -5
  23. package/{skills → rules}/doc-files/js/docgen-ignore.mjs +2 -1
  24. package/{skills → rules}/doc-files/js/docgen-scan.mjs +11 -3
  25. package/{skills → rules}/doc-files/js/docs/docgen-crc.md +1 -1
  26. package/{skills → rules}/doc-files/js/docs/docgen-extract-anchors.md +1 -1
  27. package/{skills → rules}/doc-files/js/docs/docgen-extract.md +2 -2
  28. package/{skills → rules}/doc-files/js/docs/docgen-files-batch.md +2 -2
  29. package/{skills → rules}/doc-files/js/docs/docgen-gen.md +2 -2
  30. package/{skills → rules}/doc-files/js/docs/docgen-ignore.md +4 -4
  31. package/rules/doc-files/js/docs/docgen-prompts.md +39 -0
  32. package/rules/doc-files/js/docs/docgen-scan.md +54 -0
  33. package/rules/doc-files/js/docs/lint.md +36 -0
  34. package/rules/doc-files/js/docs/units-js.md +31 -0
  35. package/rules/doc-files/js/docs/units-rs.md +35 -0
  36. package/rules/doc-files/js/docs/units.md +30 -0
  37. package/rules/doc-files/js/lint.mjs +96 -0
  38. package/{skills → rules}/doc-files/js/units-rs.mjs +37 -17
  39. package/rules/doc-files/lint/docs/lint.md +37 -0
  40. package/rules/doc-files/lint/lint.mjs +105 -0
  41. package/rules/doc-files/meta.json +1 -0
  42. package/rules/docker/docs/fix.md +21 -161
  43. package/rules/efes/docs/fix.md +23 -194
  44. package/rules/feedback/docs/fix.md +10 -8
  45. package/rules/ga/docs/fix.md +10 -5
  46. package/rules/ga/meta.json +1 -1
  47. package/rules/graphql/docs/fix.md +23 -119
  48. package/rules/hasura/docs/fix.md +19 -5
  49. package/rules/hasura/js/docs/internal_urls.md +34 -307
  50. package/rules/image-avif/docs/fix.md +16 -127
  51. package/rules/image-compress/docs/fix.md +20 -141
  52. package/rules/image-compress/js/docs/package_setup.md +22 -182
  53. package/rules/js-bun-db/docs/fix.md +23 -139
  54. package/rules/js-bun-db/js/docs/safety.md +33 -221
  55. package/rules/js-bun-redis/docs/fix.md +25 -114
  56. package/rules/js-bun-redis/js/docs/imports.md +18 -166
  57. package/rules/js-lint/docs/fix.md +30 -108
  58. package/rules/js-lint/js/docs/lint-findings.md +37 -17
  59. package/rules/js-lint/js/docs/lint.md +22 -238
  60. package/rules/js-lint/js/docs/tooling.md +34 -331
  61. package/rules/js-lint/js/lint.mjs +19 -12
  62. package/rules/js-lint/js-lint.mdc +1 -1
  63. package/rules/js-lint/meta.json +1 -1
  64. package/rules/js-lint-ci/docs/fix.md +16 -149
  65. package/rules/js-lint-ci/js/docs/lint.md +16 -136
  66. package/rules/js-lint-ci/js-lint-ci.mdc +1 -1
  67. package/rules/js-lint-ci/meta.json +1 -1
  68. package/rules/js-mssql/docs/fix.md +18 -123
  69. package/rules/js-mssql/js/docs/deps.md +28 -251
  70. package/rules/js-run/docs/fix.md +23 -138
  71. package/rules/js-run/js/docs/runtime.md +24 -378
  72. package/rules/k8s/docs/fix.md +18 -123
  73. package/rules/nginx-default-tpl/docs/fix.md +22 -118
  74. package/rules/nginx-default-tpl/js/docs/template.md +38 -360
  75. package/rules/npm-module/docs/fix.md +27 -89
  76. package/rules/npm-module/js/docs/header_doc_pointer.md +15 -15
  77. package/rules/npm-module/js/docs/package_structure.md +36 -258
  78. package/rules/npm-module/js/docs/rule_meta.md +25 -127
  79. package/rules/npm-module/js/docs/skill_meta.md +18 -180
  80. package/rules/npm-module/js/rule_meta.mjs +3 -3
  81. package/rules/php/docs/fix.md +21 -98
  82. package/rules/php/js/docs/tooling.md +20 -143
  83. package/rules/python/docs/fix.md +25 -157
  84. package/rules/python/js/docs/applies.md +20 -98
  85. package/rules/python/js/docs/tooling.md +27 -144
  86. package/rules/rego/docs/fix.md +24 -112
  87. package/rules/rego/js/docs/applies.md +20 -164
  88. package/rules/rego/js/docs/lint.md +15 -110
  89. package/rules/rego/meta.json +1 -1
  90. package/rules/release/docs/fix.md +16 -114
  91. package/rules/rust/docs/fix.md +24 -119
  92. package/rules/rust/js/docs/applies.md +20 -129
  93. package/rules/security/docs/fix.md +21 -78
  94. package/rules/security/js/docs/sample_secret.md +23 -182
  95. package/rules/security/js/docs/trufflehog.md +19 -128
  96. package/rules/security/meta.json +1 -1
  97. package/rules/style-lint/docs/fix.md +16 -150
  98. package/rules/style-lint/js/docs/lint.md +21 -172
  99. package/rules/style-lint/js/docs/tooling.md +19 -184
  100. package/rules/style-lint/js/lint.mjs +4 -3
  101. package/rules/style-lint/meta.json +1 -1
  102. package/rules/tauri/docs/fix.md +26 -152
  103. package/rules/tauri/js/docs/cargo_mutants_config.md +21 -159
  104. package/rules/tauri/js/docs/tooling.md +20 -217
  105. package/rules/test/docs/fix.md +19 -127
  106. package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +15 -127
  107. package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +17 -153
  108. package/rules/test/js/docs/cargo_mutants_config.md +24 -164
  109. package/rules/test/js/docs/location.md +24 -126
  110. package/rules/test/js/docs/no-process-chdir.md +20 -151
  111. package/rules/test/js/docs/no-relative-fs-path.md +24 -261
  112. package/rules/test/js/docs/stryker_config.md +48 -148
  113. package/rules/test/js/docs/vitest-config-pool-forks.md +21 -164
  114. package/rules/text/docs/fix.md +25 -113
  115. package/rules/text/js/docs/forbidden-prettier.md +21 -132
  116. package/rules/text/js/docs/formatting.md +60 -251
  117. package/rules/text/js/docs/lint.md +17 -114
  118. package/rules/text/js/lint.mjs +5 -3
  119. package/rules/text/lint/docs/lint.md +1 -1
  120. package/rules/text/lint/docs/run-dotenv-linter.md +1 -1
  121. package/rules/text/lint/docs/run-shellcheck.md +1 -1
  122. package/rules/text/lint/lint.mjs +13 -9
  123. package/rules/text/lint/run-dotenv-linter.mjs +13 -10
  124. package/rules/text/lint/run-shellcheck.mjs +10 -6
  125. package/rules/text/meta.json +1 -1
  126. package/rules/vue/docs/fix.md +25 -118
  127. package/rules/vue/js/docs/packages.md +25 -323
  128. package/rules/worktree/docs/fix.md +31 -150
  129. package/scripts/coverage-classify/docs/index.md +23 -209
  130. package/scripts/coverage-classify/docs/verdict-schema.md +14 -159
  131. package/scripts/dispatcher/docs/trace.md +35 -0
  132. package/scripts/docs/auto-rules.md +37 -361
  133. package/scripts/docs/lint-cli.md +12 -13
  134. package/scripts/docs/post-tool-use-fix.md +16 -15
  135. package/scripts/docs/skills-cli.md +26 -23
  136. package/scripts/docs/sync-claude-config.md +94 -34
  137. package/scripts/docs/worktree-cli.md +11 -34
  138. package/scripts/lib/docs/assert-project-root.md +14 -16
  139. package/scripts/lib/docs/changed-files.md +24 -139
  140. package/scripts/lib/docs/discover-check-rules-from-cursor.md +14 -146
  141. package/scripts/lib/docs/rule-meta.md +1 -1
  142. package/scripts/lib/docs/rule-predicates.md +20 -17
  143. package/scripts/lib/docs/run-rule-cli.md +14 -18
  144. package/scripts/lib/docs/run-rule.md +13 -20
  145. package/scripts/lib/docs/run-standard-rule.md +12 -15
  146. package/scripts/lib/docs/sync-gitignore-worktree.md +15 -18
  147. package/scripts/lib/rule-meta.mjs +10 -6
  148. package/scripts/lib/rule-predicates.mjs +1 -1
  149. package/scripts/lint-cli.mjs +28 -20
  150. package/scripts/sync-claude-config.mjs +4 -1
  151. package/scripts/utils/docs/with-lock.md +19 -12
  152. package/scripts/utils/with-lock.mjs +4 -2
  153. package/skills/doc-aggregate/SKILL.md +2 -2
  154. package/skills/doc-aggregate/js/docgen-ignore.mjs +6 -6
  155. package/skills/doc-aggregate/js/docs/docgen-ignore.md +1 -1
  156. package/skills/doc-aggregate/js/docs/docgen-scan.md +78 -0
  157. package/skills/doc-files/.changes/260612-0031.md +5 -0
  158. package/skills/doc-files/.changes/260612-0036.md +5 -0
  159. package/skills/doc-files/.changes/260612-0114.md +5 -0
  160. package/skills/doc-files/SKILL.md +6 -6
  161. package/skills/fix/js/docs/llm-worker.md +17 -15
  162. package/skills/fix/js/docs/orchestrator.md +30 -23
  163. package/skills/fix/js/docs/t0.md +26 -16
  164. package/skills/start-check/js/docs/check.md +26 -22
  165. package/skills/taze/js/docs/diff.md +44 -20
  166. package/skills/doc-files/js/docs/docgen-prompts.md +0 -32
  167. package/skills/doc-files/js/docs/docgen-scan.md +0 -25
  168. package/skills/doc-files/js/docs/units-rs.md +0 -35
  169. /package/{skills → rules}/doc-files/js/docgen-crc.mjs +0 -0
  170. /package/{skills → rules}/doc-files/js/docgen-extract-anchors.mjs +0 -0
  171. /package/{skills → rules}/doc-files/js/docgen-prompts.mjs +0 -0
  172. /package/{skills → rules}/doc-files/js/units-js.mjs +0 -0
  173. /package/{skills → rules}/doc-files/js/units.mjs +0 -0
@@ -1,218 +1,32 @@
1
- # `coverage-classify/index.mjs`
1
+ ---
2
+ docgen:
3
+ source: npm/scripts/coverage-classify/index.mjs
4
+ crc: 06249ac8
5
+ score: 100
6
+ ---
2
7
 
3
- ## Огляд
4
-
5
- Модуль `coverage-classify/index.mjs` — це **публічна точка входу** (Public API) класифікатора survived-мутантів за допомогою Claude API від Anthropic. Він відповідає за оркестрацію LLM-класифікації мутантів, які пережили mutation-testing раунд (тобто не були вбиті жодним тестом), і повертає для кожного з них вердикт у форматі `{ verdict, confidence, reason, suggestedTest? }`.
6
-
7
- Призначення модуля — допомогти автоматичним інструментам типу `/n-coverage-fix` ухвалювати рішення, чи варто дописувати тест на конкретний survived-мутант, чи можна його ігнорувати як equivalent / not-worth-testing.
8
-
9
- Ключові архітектурні характеристики:
10
-
11
- - **Graceful degradation:** відсутність змінної `ANTHROPIC_API_KEY` або непрацездатний dynamic import пакета `@anthropic-ai/sdk` не приводить до краху — функція мовчки повертає порожній масив `[]`, попередньо вивівши попередження у `console.warn`.
12
- - **Persistent cache:** результати класифікації кешуються на диск через утиліти з `./cache.mjs`. При зміні `MODEL` кеш повністю інвалідується (entries чистяться, поле `model` оновлюється). Це гарантує консистентність вердиктів між запусками за умови незмінного контексту мутанта.
13
- - **Prompt caching на стороні API:** системний промпт передається з `cache_control: { type: 'ephemeral' }` — усі мутанти одного прогону reuse кешований префікс на стороні Anthropic API (економія токенів).
14
- - **Retry з експоненційним backoff:** кожен мутант отримує до `MAX_RETRIES + 1 = 3` спроб; між спробами — затримка `retryDelayMs * 2 ** attempt`. Після вичерпання спроб — повертається **conservative fallback** `worth-testing/confidence=0`, щоб не втратити мутант з виду.
15
-
16
- Модуль не виконує мережевих викликів самостійно — він делегує їх в `client.messages.create(...)` (за замовчуванням це інстанс `new Anthropic()` з SDK).
17
-
18
- ## Експорти / API
19
-
20
- | Експорт | Тип | Опис |
21
- | ---------- | -------- | ------------------------------------------------------------------------------------------------ |
22
- | `classify` | function | Іменований async-експорт. Класифікує всіх survived-мутантів і повертає масив `{ key, verdict }`. |
23
-
24
- Інші ідентифікатори файлу (`MODEL`, `MAX_RETRIES`, `DEFAULT_RETRY_DELAY_MS`, `FALLBACK_VERDICT`, `classifyOne`) — **внутрішні**, не експортуються.
25
-
26
- ### Сигнатура `classify`
27
-
28
- ```js
29
- export async function classify(survived, cwd, opts = {})
30
- ```
31
-
32
- **Параметри:**
33
-
34
- - `survived` — `Array<{ file: string, mutants: Array<object>, exampleTest?: object|null, recommendationText?: string|null }>` — список survived-груп. Структура аналогічна до використовуваної в `COVERAGE.md` (звіт mutation-testing). Кожна група відповідає одному файлу та містить масив мутантів.
35
- - `cwd` — `string` — абсолютний шлях кореня проєкту. Використовується для:
36
- - формування дефолтного шляху кешу (`<cwd>/npm/reports/coverage-classify.cache.json`);
37
- - резолвінгу шляхів до файлів-джерел мутантів (для побудови юзер-промпта та cache key).
38
- - `opts` — `{ cachePath?: string, client?: object, retryDelayMs?: number }` — опціональні ін'єкції для тестування:
39
- - `cachePath` — кастомний шлях до файлу кешу.
40
- - `client` — підставний Anthropic SDK client (для unit-тестів без реальних мережевих викликів). Має мати метод `messages.create(...)`.
41
- - `retryDelayMs` — базова затримка для exp-backoff у мс. `0` фактично вимикає sleep між retries (зручно для тестів).
42
-
43
- **Повертає:** `Promise<Array<{ key: string, verdict: object }>>`
44
-
45
- - `key` — рядок формату `<file>:<line>:<col>:<replacement>`, який однозначно ідентифікує мутант для зовнішнього коду.
46
- - `verdict` — об'єкт з полями `{ verdict, confidence, reason, suggestedTest? }` (формат описаний у `./verdict-schema.mjs`).
47
-
48
- При відсутності API-ключа або SDK — повертає `[]`.
49
-
50
- ## Функції
51
-
52
- ### `classify(survived, cwd, opts)` — публічна
53
-
54
- **Сигнатура:** `async function classify(survived, cwd, opts = {}) -> Promise<Array<{key, verdict}>>`
55
-
56
- **Параметри:** див. розділ «Експорти / API» вище.
57
-
58
- **Повертає:** `Promise<Array<{ key: string, verdict: object }>>` — плаский список вердиктів по всіх мутантах усіх груп (порядок: group-by-group, mutant-by-mutant у межах групи).
59
-
60
- **Кроки виконання (orchestration):**
61
-
62
- 1. Резолвить `cachePath` (дефолт: `<cwd>/npm/reports/coverage-classify.cache.json`) та `retryDelayMs` (дефолт: `DEFAULT_RETRY_DELAY_MS = 1000`).
63
- 2. Перевіряє `env.ANTHROPIC_API_KEY`. Якщо відсутній — `console.warn` + `return []`.
64
- 3. Виконує `await import('@anthropic-ai/sdk')`. Якщо пакет не встановлено — `console.warn` + `return []`.
65
- 4. Створює клієнт: `opts.client ?? new Anthropic()`.
66
- 5. Завантажує кеш через `readCache(cachePath)`. Якщо `cache.model !== MODEL` — повністю чистить `cache.entries` і виставляє `cache.model = MODEL` (інвалідація при зміні моделі).
67
- 6. Ітерує `survived.mutants`:
68
- - Будує `lookupKey = "<group.file>:<line>:<col>:<replacement>"`.
69
- - Обчислює `cacheKey = deriveCacheKey(join(cwd, group.file), mutant)`.
70
- - Якщо в кеші є запис — повертає його (нормалізуючи поля, опціонально розгортаючи `suggestedTest`).
71
- - Інакше викликає `classifyOne(client, group, mutant, cwd, retryDelayMs)` і записує результат у `cache.entries[cacheKey]` з полем `classifiedAt: new Date().toISOString()` (якщо `cacheKey` truthy).
72
- 7. Зберігає кеш на диск через `writeCache(cachePath, cache)`.
73
- 8. Повертає накопичений `verdicts`.
74
-
75
- **Side effects:**
76
-
77
- - **Disk I/O:** читання і запис файлу кешу (`<cwd>/npm/reports/coverage-classify.cache.json` за замовчуванням).
78
- - **Мережа:** виклики `client.messages.create(...)` до Anthropic API (через делегування в `classifyOne`).
79
- - **stdout/stderr:** `console.warn(...)` при відсутності ключа / SDK / при фатальних retry-фейлах окремих мутантів.
80
- - **Час:** функція **очікує** мережеві запити послідовно (no parallelism між мутантами) — у гіршому випадку загальна тривалість = `N × (MAX_RETRIES+1) × API_latency + sum(backoff)`.
81
- - **Стан системи:** мутації `cache` на місці (об'єкт переписується), потім дамп на диск.
82
-
83
- ### `classifyOne(client, group, mutant, cwd, retryDelayMs)` — внутрішня
84
-
85
- **Сигнатура:** `async function classifyOne(client, group, mutant, cwd, retryDelayMs) -> Promise<object>`
8
+ # index.mjs
86
9
 
87
- **Параметри:**
88
-
89
- - `client` — `{ messages: { create: Function } }` — Anthropic SDK client (реальний або mock).
90
- - `group` — `{ file: string }` — група для контексту (з неї потрібно лише `.file` — повний шлях до файлу-джерела).
91
- - `mutant` — `object` — дані мутанта (передаються в `buildUserPrompt` як `{ ...mutant, file: group.file }`).
92
- - `cwd` — `string` — корінь проєкту, потрібний для resolving шляхів у `buildUserPrompt`.
93
- - `retryDelayMs` — `number` — базова затримка для exp-backoff (`0` у тестах вимикає sleep).
94
-
95
- **Повертає:** `Promise<object>` — розпарсений вердикт (через `parseVerdict(text)`) або копія `FALLBACK_VERDICT`.
96
-
97
- **Кроки виконання:**
98
-
99
- 1. Будує юзер-промпт: `userPrompt = buildUserPrompt({ ...mutant, file: group.file }, cwd)`.
100
- 2. Цикл `for (attempt = 0; attempt <= MAX_RETRIES; attempt++)`:
101
- - Виконує `client.messages.create({ model: MODEL, max_tokens: 1024, system: [{ type:'text', text: SYSTEM_PROMPT, cache_control:{ type:'ephemeral' } }], messages: [{ role:'user', content: userPrompt }] })`.
102
- - Дістає текст з `response?.content?.[0]?.text ?? ''`.
103
- - Повертає `parseVerdict(text)`. Якщо `parseVerdict` кидає — це впаде як виключення і потрапить в `catch` цієї ж ітерації (тобто буде ще одна retry-спроба).
104
- 3. На `catch`: запам'ятовує `lastError`. Якщо `attempt < MAX_RETRIES && retryDelayMs > 0` — `await setTimeout(retryDelayMs * 2 ** attempt)`.
105
- 4. Після вичерпання спроб: `console.warn` з деталями (file:line:col, кількість спроб, повідомлення останньої помилки) і `return { ...FALLBACK_VERDICT }` (копія, щоб уникнути shared mutation на константі).
106
-
107
- **Side effects:**
108
-
109
- - Мережевий виклик до Anthropic API (1..MAX_RETRIES+1 разів).
110
- - `setTimeout` з `node:timers/promises` (async sleep).
111
- - `console.warn` при фатальному фейлі.
112
-
113
- ## Залежності
114
-
115
- ### Внутрішні (relative imports)
116
-
117
- | Шлях | Що використовується | Призначення |
118
- | ---------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
119
- | `./cache.mjs` | `deriveCacheKey`, `readCache`, `writeCache` | Робота з персистентним кешем класифікацій (deriving детермінованого ключа з мутанта + читання/запис JSON). |
120
- | `./prompt.mjs` | `buildUserPrompt`, `SYSTEM_PROMPT` | Побудова промптів для LLM: системний (статичний, кешується) та юзер-промпт (динамічний, per-mutant). |
121
- | `./verdict-schema.mjs` | `parseVerdict` | Парсинг та валідація JSON-відповіді LLM у структурований об'єкт вердикту. |
122
-
123
- ### Зовнішні
124
-
125
- | Пакет/модуль | Що використовується | Як підключено |
126
- | ---------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------- |
127
- | `node:path` | `join` | Статичний `import`. Збирання шляхів до cache file та source file. |
128
- | `node:process` | `env` | Статичний `import`. Читання `ANTHROPIC_API_KEY`. |
129
- | `node:timers/promises` | `setTimeout` | Статичний `import`. Async sleep для exp-backoff між retries. |
130
- | `@anthropic-ai/sdk` | `Anthropic` (default export) | **Dynamic** `await import(...)` всередині `classify` — graceful degradation, якщо пакет не встановлено. |
131
-
132
- ### Зовнішні артефакти середовища
133
-
134
- - **Змінна оточення `ANTHROPIC_API_KEY`** — обов'язкова. Без неї модуль не робить мережевих викликів.
135
- - **Файл кешу** — за замовчуванням `<cwd>/npm/reports/coverage-classify.cache.json`. Структура: `{ model: string, entries: { [cacheKey]: { verdict, confidence, reason, suggestedTest?, classifiedAt } } }`.
136
-
137
- ### Константи модуля
138
-
139
- | Константа | Значення | Призначення |
140
- | ------------------------ | ------------------------------------------------------------ | --------------------------------------------------------------------------- |
141
- | `MODEL` | `'claude-sonnet-4-6'` | ID моделі Anthropic для класифікації. Зміна → автоматична інвалідація кешу. |
142
- | `MAX_RETRIES` | `2` | Кількість **повторних** спроб (всього спроб: `MAX_RETRIES + 1 = 3`). |
143
- | `DEFAULT_RETRY_DELAY_MS` | `1000` | Базова затримка для exp-backoff (мс). Реальні delays: 1000, 2000, 4000. |
144
- | `FALLBACK_VERDICT` | `{ verdict: 'worth-testing', confidence: 0, reason: '...' }` | Консервативний вердикт при фатальному фейлі (мутант ще не відкидається). |
145
-
146
- ## Потік виконання / Використання
147
-
148
- ### Типовий сценарій виклику
149
-
150
- Модуль викликається з вищерівневого pipeline (наприклад, `/n-coverage-fix` або інший CLI поверх mutation-testing звіту):
151
-
152
- ```js
153
- import { classify } from '<repo>/npm/scripts/coverage-classify/index.mjs'
154
-
155
- // survived зазвичай парситься з COVERAGE.md
156
- const survived = [
157
- {
158
- file: 'src/utils/foo.mjs',
159
- mutants: [
160
- { line: 10, col: 5, replacement: '=== 0', original: '!== 0', mutator: 'EqualityOperator' }
161
- // ...
162
- ],
163
- exampleTest: null,
164
- recommendationText: null
165
- }
166
- // ...
167
- ]
168
-
169
- const verdicts = await classify(survived, process.cwd())
170
-
171
- for (const { key, verdict } of verdicts) {
172
- if (verdict.verdict === 'worth-testing' && verdict.confidence > 0.5) {
173
- // дописати тест
174
- }
175
- }
176
- ```
177
-
178
- ### Стани виходу та граничні випадки
179
-
180
- | Стан | Поведінка |
181
- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------ |
182
- | `ANTHROPIC_API_KEY` не виставлений | `console.warn` + `return []` (порожній масив, не помилка). |
183
- | Пакет `@anthropic-ai/sdk` не встановлено | `console.warn` + `return []`. |
184
- | Кеш hit для мутанта | Мережевий виклик не виконується, повертається cached verdict. |
185
- | Кеш miss, успішна класифікація | Виклик API, парсинг, запис у кеш, push у verdicts. |
186
- | Кеш miss, всі retry-спроби впали | `console.warn` з деталями + `FALLBACK_VERDICT` push у verdicts (мутант не пропадає). |
187
- | Зміна `MODEL` у коді | На наступному запуску `cache.entries` повністю обнуляється. |
188
- | `cacheKey === null/undefined` (наприклад, неможливо derive) | Класифікація виконується, але **не** кешується (запис у кеш пропускається). |
189
-
190
- ### Послідовність всередині одного прогону (timeline)
10
+ ## Огляд
191
11
 
192
- 1. Читання `survived` (відповідальність caller'а).
193
- 2. `classify(...)` запускається → preflight перевірки (API key, SDK).
194
- 3. Завантаження дискового кешу → можливе очищення при зміні моделі.
195
- 4. **Послідовно** (не паралельно) для кожного `(group, mutant)`:
196
- - Cache lookup (за `cacheKey`).
197
- - Якщо miss — `classifyOne` з retry-логікою.
198
- - Запис у кеш-об'єкт у пам'яті.
199
- 5. Після обходу всіх мутантів — атомарний `writeCache` на диск.
200
- 6. Повернення `verdicts` caller'у.
12
+ Файл забезпечує класифікацію записів через послідовну спробу отримання результату з кешу, первинного моделі та хмарного сервісу. Якщо кеш не містить даних, відбувається перехід до наступного рівня, доки не буде досягнуто консервативного запасного варіанту. Результати класифікації записуються у кеш. Код спирається на конфіги з файлу coverage-classify.cache.json.
201
13
 
202
- ### Особливості та інваріанти
14
+ ## Поведінка
203
15
 
204
- - **Послідовність викликів API:** немає паралелізму між мутантами. Це навмисно — обмеження по rate-limits Anthropic API та для стабільної взаємодії з prompt cache.
205
- - **Prompt cache reuse:** оскільки системний промпт ідентичний для всіх мутантів і помічений `cache_control: ephemeral`, Anthropic API повторно використовує кешований префікс — суттєва економія input-токенів на великих прогонах.
206
- - **Ідемпотентність:** повторний запуск з тим же `cachePath` і незмінним `survived` дає той же `verdicts` без додаткових мережевих викликів (повний cache hit).
207
- - **Conservative fallback:** при фейлі класифікації мутант **не** відкидається — він отримує `worth-testing/confidence=0`, що змушує caller свідомо вирішувати, чи приймати такий вердикт. Це усуває ризик «непомітної втрати» мутанта через мережеву помилку.
208
- - **Defensive copy fallback:** повертається `{ ...FALLBACK_VERDICT }`, а не сам об'єкт щоб caller'и не могли випадково замутувати константу.
16
+ 1. Пошук результату в кеші.
17
+ 2. Якщо результат знайдено, використовується збережений вердикт.
18
+ 3. Якщо результат не знайдено, намагається отримати вердикт через перший рівень.
19
+ 4. Якщо перший рівень не вдається, намагається отримати вердикт через другий рівень.
20
+ 5. Якщо другий рівень не вдається, використовується консервативний запасний варіант.
21
+ 6. Результати класифікації записуються в кеш.
209
22
 
210
- ### Тестування
23
+ ## Публічний API
211
24
 
212
- Для unit-тестів зручно ін'єктувати:
25
+ classify Класифікує survived мутанти, перенаправляючи їх до CLOUD_MIN, якщо результат resolveModel дорівнює CLOUD_MIN, інакше до fallback.
213
26
 
214
- - `opts.client = { messages: { create: async () => ({ content: [{ text: '...' }] }) } }` — підставний клієнт.
215
- - `opts.cachePath = '/tmp/test-cache.json'` — ізольований кеш.
216
- - `opts.retryDelayMs = 0` — миттєві retries без `setTimeout`-блокувань.
27
+ ## Гарантії поведінки
217
28
 
218
- Файл `index.mjs` спроєктований так, що жодних інших залежностей мокати **не потрібно** — `cache.mjs`/`prompt.mjs`/`verdict-schema.mjs` мають детерміновану поведінку для тестів.
29
+ - Read-only: файл не виконує операцій запису у файлову систему.
30
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
31
+ - Кешує результати в межах одного прогону.
32
+ - Не звертається до мережі.
@@ -1,175 +1,30 @@
1
1
  ---
2
2
  docgen:
3
3
  source: npm/scripts/coverage-classify/verdict-schema.mjs
4
- crc: 8a2c0df1
4
+ crc: ecf5dfe1
5
+ score: 100
5
6
  ---
6
7
 
7
8
  # verdict-schema.mjs
8
9
 
9
10
  ## Огляд
10
11
 
11
- Модуль `verdict-schema.mjs` визначає контракт даних для відповіді LLM-класифікатора в межах інструменту `coverage-classify`. Класифікатор приймає рішення про те, як трактувати вцілілого мутанта (surviving mutant) після прогону мутаційного тестування: чи варто на нього писати тест, чи він поведінково еквівалентний, чи це захисна гілка для неможливого стану, чи це склейка/обгортка, що покривається інтеграційними тестами.
12
+ Файл надає схему `VerdictSchema` для валідації вердиктів LLM-класифікатора. Функція `parseVerdict` витягує JSON-об'єкт з сирої текстової відповіді моделі та перевіряє його відповідність визначеній схемі.
12
13
 
13
- Файл розв'язує дві задачі:
14
+ ## Поведінка
14
15
 
15
- 1. Декларує **Zod-схему** `VerdictSchema`, якій має відповідати JSON-об'єкт-вердикт від LLM.
16
- 2. Надає утиліту `parseVerdict`, яка приймає сирий текст відповіді LLM, витягує з нього перший знайдений JSON-блок, парсить його і валідовує за схемою.
16
+ VerdictSchema
17
+ Визначає схему для валідації вердикту LLM-класифікатора
17
18
 
18
- Призначення схеми — стабілізувати взаємодію з LLM: навіть якщо LLM повертає вердикт у вигляді тексту з преамбулою чи поясненнями, модуль робить «liberal-in, strict-out» — толерантно витягує JSON-острівець, а далі застосовує жорстку валідацію (категорія, межі впевненості, мінімальна осмисленість пояснення).
19
+ parseVerdict
20
+ Витягує JSON-об'єкт з текстової відповіді LLM і валідує його за схемою
19
21
 
20
- Категорії вердикту (`verdict`):
22
+ ## Публічний API
21
23
 
22
- - `worth-testing` pure logic / реальні гілки, варто писати юніт-тест.
23
- - `equivalent` мутант поведінково еквівалентний оригіналу, він не killable.
24
- - `defensive` — гілка для impossible state (захисний код), не killable.
25
- - `glue` — CLI entry / обгортка `runStandardRule` тощо; покривається інтеграційними тестами.
26
- - `wrapper` — тонкий `spawn`/`fetch`-обгортка; покривається інтеграційними тестами.
24
+ VerdictSchemaСхема для структури вердикту.
25
+ parseVerdictВитягує JSON з тексту LLM і перевіряє його за схемою VerdictSchema.
27
26
 
28
- ## Експорти / API
27
+ ## Гарантії поведінки
29
28
 
30
- Модуль експортує два символи (обидва іменовані експорти ES modules):
31
-
32
- | Символ | Тип | Призначення |
33
- | --------------- | --------------------------- | ---------------------------------------------------- |
34
- | `VerdictSchema` | `ZodObject` | Схема валідації для verdict-об'єкта LLM. |
35
- | `parseVerdict` | `function(rawText: string)` | Парсер raw-text відповіді LLM у валідований verdict. |
36
-
37
- ### `VerdictSchema`
38
-
39
- Z-об'єкт зі строго визначеними полями:
40
-
41
- - `verdict` — `z.enum(['worth-testing', 'equivalent', 'defensive', 'glue', 'wrapper'])`. Обов'язкове. Будь-яке інше значення відхиляється.
42
- - `confidence` — `z.number().min(0).max(1)`. Обов'язкове. Дробове число в діапазоні `[0, 1]`, де `0` — повна невпевненість, `1` — повна впевненість LLM у вердикті.
43
- - `reason` — `z.string().min(20).max(500)`. Обов'язкове. Текстове пояснення довжиною від 20 до 500 символів — мінімум змушує LLM аргументувати, максимум — обмежує балакучість.
44
- - `suggestedTest` — `z.string().max(300).optional()`. Опціональне. Текстова підказка/начерк тесту довжиною до 300 символів; за домовленістю заповнюється переважно для категорії `worth-testing`.
45
-
46
- Поля поза цим переліком не описано як заборонені на рівні схеми; за замовчуванням Zod пропускає невідомі ключі без помилки, але вони не з'являться в результаті типізації, що використовує цей модуль. Якщо в майбутньому потрібен strict-режим, його можна додати через `.strict()`.
47
-
48
- ### `parseVerdict(rawText)`
49
-
50
- Високорівнева функція-фасад: «витягни JSON з тексту LLM і провалідуй».
51
-
52
- ## Функції
53
-
54
- ### `parseVerdict(rawText)`
55
-
56
- Сигнатура:
57
-
58
- ```js
59
- function parseVerdict(rawText: string): {
60
- verdict: 'worth-testing' | 'equivalent' | 'defensive' | 'glue' | 'wrapper',
61
- confidence: number,
62
- reason: string,
63
- suggestedTest?: string
64
- }
65
- ```
66
-
67
- Параметри:
68
-
69
- - `rawText` — `string`. Сирий текст, повернений LLM. Може містити довільний preamble/postamble навколо JSON-блоку (пояснення, markdown-загортки тощо). Очікується, що в тексті є рівно один корисний JSON-об'єкт-вердикт.
70
-
71
- Повертає:
72
-
73
- - Обʼєкт verdict, який пройшов валідацію `VerdictSchema`. Тип повернення відповідає схемі (див. вище).
74
-
75
- Side effects:
76
-
77
- - Жодних. Функція чиста: не пише в файлову систему, не звертається до мережі, не модифікує параметри.
78
-
79
- Алгоритм:
80
-
81
- 1. Знайти позицію першого символу `{` через `rawText.indexOf('{')` → `jsonStart`.
82
- 2. Знайти позицію останнього `}` через `rawText.lastIndexOf('}')` → `jsonEnd`.
83
- 3. Якщо хоча б один з індексів дорівнює `-1` — кинути `Error('No JSON object found in LLM response')`.
84
- 4. Інакше — вирізати підрядок `rawText.slice(jsonStart, jsonEnd + 1)` (включно з обома фігурними дужками) та віддати у `JSON.parse`.
85
- 5. Результат парсингу передати у `VerdictSchema.parse(...)` і повернути.
86
-
87
- Виключення / помилки:
88
-
89
- - `Error('No JSON object found in LLM response')` — якщо в тексті немає жодної з фігурних дужок (`{` або `}`).
90
- - `SyntaxError` (від `JSON.parse`) — якщо вирізаний фрагмент не є валідним JSON. Наприклад, через зайвий текст усередині `{...}`, який LLM могла додати між дужками.
91
- - `ZodError` (від `VerdictSchema.parse`) — якщо JSON валідний, але не відповідає схемі: невідома категорія `verdict`, `confidence` поза `[0, 1]`, `reason` коротший за 20 символів або довший за 500, `suggestedTest` довший за 300 символів, відсутнє обов'язкове поле тощо.
92
-
93
- Поведінкові нюанси, важливі для викликача:
94
-
95
- - Стратегія «перший `{` … останній `}`» свідомо толерантна: дозволяє LLM писати щось ось так: `Here's the verdict: { ... } Hope it helps.` Однак якщо LLM повертає **кілька** JSON-обʼєктів, функція ризикує склеїти їх у невалідний фрагмент — це очікувано і впирається у валідацію JSON.
96
- - Функція не намагається «латати» некоректний JSON (немає trailing-comma-cleanup, немає markdown-fence-strip). Помилки прокидаються догори викликача — там і місце для retry / fallback-логіки.
97
- - У разі `ZodError` повідомлення зазвичай містить читабельний шлях до невалідного поля, що зручно для логування при debug LLM-промпта.
98
-
99
- ## Залежності
100
-
101
- Зовнішні:
102
-
103
- - [`zod`](https://www.npmjs.com/package/zod) — рантайм-валідатор схем. Імпортується як `import { z } from 'zod'`. Версія керується кореневим `package.json` робочого простору `npm/`.
104
-
105
- Внутрішні:
106
-
107
- - Немає. Модуль не імпортує нічого з власного проєкту і не залежить від інших файлів `coverage-classify`.
108
-
109
- Стандартна бібліотека / середовище:
110
-
111
- - `JSON.parse` — глобальний API JavaScript runtime.
112
- - `String.prototype.indexOf` / `lastIndexOf` / `slice` — стандартні методи рядків.
113
-
114
- Цільове середовище: Node-сумісний рантайм (Node.js / Bun) із підтримкою ES Modules — файл написано як `.mjs`.
115
-
116
- ## Потік виконання / Використання
117
-
118
- ### Як модуль вписується в `coverage-classify`
119
-
120
- Інструмент `coverage-classify` працює за такою послідовністю (типова):
121
-
122
- 1. Зчитати список вцілілих мутантів із результатів мутаційного тестування.
123
- 2. Для кожного мутанта зібрати контекст (фрагмент коду, місце змін) та згенерувати промпт для LLM.
124
- 3. Виконати запит до LLM і отримати raw-text відповідь.
125
- 4. **Викликати `parseVerdict(rawText)`** із цього модуля, щоб отримати типізований verdict.
126
- 5. Зберегти verdict у кеш / звіт (`apply.mjs`, `cache.mjs`) і застосувати дії: для `worth-testing` згенерувати TODO/тести, для решти — позначити мутант відповідною категорією.
127
-
128
- Таким чином, `verdict-schema.mjs` — це межа між «світом LLM» (нечіткий текст) і «світом інструменту» (структуровані дані).
129
-
130
- ### Приклад використання
131
-
132
- ```js
133
- import { parseVerdict, VerdictSchema } from './verdict-schema.mjs'
134
-
135
- // 1) Парсимо raw-відповідь LLM
136
- const rawLLM = `
137
- The mutant is in a defensive branch. Here is the verdict:
138
- {
139
- "verdict": "defensive",
140
- "confidence": 0.92,
141
- "reason": "The condition guards an impossible state when the input list is empty.",
142
- "suggestedTest": ""
143
- }
144
- Thanks!
145
- `
146
-
147
- try {
148
- const verdict = parseVerdict(rawLLM)
149
- // verdict.verdict === 'defensive'
150
- // verdict.confidence === 0.92
151
- console.log(verdict)
152
- } catch (err) {
153
- // 'No JSON object found in LLM response' | SyntaxError | ZodError
154
- console.error('LLM verdict invalid:', err.message)
155
- }
156
-
157
- // 2) Якщо в нас уже є об'єкт (наприклад, з кешу) — валідуємо безпосередньо через схему
158
- const cached = { verdict: 'glue', confidence: 1, reason: 'CLI entry point only, integration covers.' }
159
- const ok = VerdictSchema.parse(cached) // кине ZodError, якщо не відповідає
160
- ```
161
-
162
- ### Граничні випадки
163
-
164
- - Порожній рядок → `indexOf('{') === -1` → кидаємо `'No JSON object found in LLM response'`.
165
- - Рядок без `}` (LLM обірвалась) → `lastIndexOf('}') === -1` → та сама помилка.
166
- - LLM повернула markdown-fence (` ```json ... ``` `) навколо JSON → `parseVerdict` витягне саме JSON-острів між першим `{` і останнім `}`, fence ігнорується.
167
- - LLM повернула `confidence: 1.5` → `ZodError` (порушено `max(1)`).
168
- - LLM повернула `verdict: 'unknown'` → `ZodError` (не входить у `z.enum([...])`).
169
- - LLM повернула `reason` коротшим за 20 символів → `ZodError` (`min(20)`). Це навмисний захист від відповідей-однословів типу `"ok"`.
170
-
171
- ### Рекомендації для викликача
172
-
173
- - Обгортайте виклик `parseVerdict` у `try/catch` і за помилки робіть retry з посиленим промптом (наприклад, з нагадуванням про схему) або падайте у fallback-категорію.
174
- - Не зберігайте у кеш `rawText` як вердикт — зберігайте лише провалідований об'єкт (результат `parseVerdict`); це підтримує інваріант «у кеші тільки валідні дані».
175
- - Якщо в майбутньому з'являться нові категорії — оновлюйте `z.enum` тут, інакше LLM-вердикти з новими значеннями будуть відхилятися.
29
+ - Read-only: файл не виконує операцій запису у файлову систему.
30
+ - Не звертається до мережі.
@@ -0,0 +1,35 @@
1
+ ---
2
+ docgen:
3
+ source: npm/scripts/dispatcher/trace.mjs
4
+ crc: 8d541669
5
+ score: 100
6
+ ---
7
+
8
+ # trace.mjs
9
+
10
+ ## Огляд
11
+
12
+ Файл забезпечує наскрізну простежуваність артефактів. Він читає front-matter з директорій `docs/{tasks,specs,plans,adr}`, будує ланцюг за лінками та позначає розриви. Функції генерують аналіз, який потім рендериться у текстовий вивід, а трасування виконується для зчитування артефактів.
13
+
14
+ ## Поведінка
15
+
16
+ parseFrontMatter Парсить плаский YAML-front-matter. Не обробляє вкладеність. Відрізає інлайн-коментарі.
17
+
18
+ analyze Будує аналіз. Для кожного артефакту генерує лінки з позначкою про розрив.
19
+
20
+ render Текстовий рендер аналізу. Форматує вивід для читання.
21
+
22
+ runTraceCli Виконує трасування. Зчитує артефакти з директорій. Будує аналіз. Повертає код виходу.
23
+
24
+ ## Публічний API
25
+
26
+ parseFrontMatter — Парсить плаский YAML-front-matter (key: value). Обробляє лише основні поля spec/plan/task-record. Видаляє інлайн-коментарі.
27
+ analyze — Створює аналіз: для кожного артефакту — зв'язок його лінків зі статусом ok/розрив. `breaking` означає відсутність лінка, що руйнує ланцюг.
28
+ render — Відображає текстовий вивід аналізу.
29
+ runTraceCli — Виконує команду CLI `n-cursor trace [--json]`. Повертає 1, якщо виявлено розриви ланцюга.
30
+
31
+ ## Гарантії поведінки
32
+
33
+ - Read-only: файл не виконує операцій запису у файлову систему.
34
+ - За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
35
+ - Не звертається до мережі.