@7n/rules 1.40.1 → 1.42.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/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/rules/doc-files/check/docs/fix-worker.md +4 -1
- package/rules/doc-files/check/fix-worker.mjs +6 -0
- package/rules/doc-files/docgen-judge/docs/main.md +2 -2
- package/rules/doc-files/docgen-judge/main.mjs +9 -3
- package/rules/doc-files/main.mdc +6 -2
- package/scripts/lib/lint-surface/docs/run-detectors.md +3 -1
- package/scripts/lib/lint-surface/run-detectors.mjs +71 -9
- package/rules/doc-files/check/docs/fix-check.md +0 -28
- package/rules/doc-files/check/fix-check.mjs +0 -45
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.42.0] - 2026-07-22
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- warnAboutRulesWithoutConcerns: попередження, якщо rule-id з .n-rules.json#rules не знайдено в жодному rulesDir (ядро+плагіни) — ловить дрейф конфігу після переїзду concern-ів у плагін
|
|
8
|
+
|
|
9
|
+
## [1.41.0] - 2026-07-22
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- doc-files: прибрано безумовний T0 CRC-штамп для crc-mismatch (fix-check.mjs) — свіжий CRC поверх застарілого тексту назавжди маскував дрейф доки; тепер застаріла дока завжди регенерується fix-worker-ом (docgen). Guardrail: detectRefusalFiller ловить нові живі refusal-фрази локальної моделі («мені потрібен сам код», «щоб написати точну документацію», «I need the code»)
|
|
14
|
+
|
|
3
15
|
## [1.40.1] - 2026-07-22
|
|
4
16
|
|
|
5
17
|
### Fixed
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ type: JS Module
|
|
|
3
3
|
title: fix-worker.mjs
|
|
4
4
|
resource: npm/rules/doc-files/check/fix-worker.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 452ab360
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
issues: judge:inaccurate:0.98
|
|
@@ -13,6 +13,8 @@ docgen:
|
|
|
13
13
|
|
|
14
14
|
Файл забезпечує генерацію застарілих або відсутніх файлових доків за допомогою docgen-pipeline (локальна/хмарна модель) через функцію fixWorker. Кожна згенерована дока реєструється як durable-write (самодостатній кінцевий стан зі свіжим CRC), тому rollback провального rung-а її не стирає, а великий беклог сходиться за кілька прогонів (issue nitra/cursor#16). Видалення сирітських доків лишається під звичайним rollback.
|
|
15
15
|
|
|
16
|
+
Інваріант конвеєра: `crc-mismatch` не закривається детермінованим T0-штампом CRC (fix-check.mjs для check свідомо відсутній) — свіжий CRC поверх старого тексту назавжди маскував би дрейф доки. Застаріла дока завжди йде через регенерацію тут; свіжий CRC зʼявляється лише разом зі щойно згенерованим вмістом (stampDoc усередині runGenerationBatch).
|
|
17
|
+
|
|
16
18
|
## Поведінка
|
|
17
19
|
|
|
18
20
|
1. Викликається `fixWorker`.
|
|
@@ -26,3 +28,4 @@ docgen:
|
|
|
26
28
|
## Гарантії поведінки
|
|
27
29
|
|
|
28
30
|
- Кожна записана дока — валідний фінальний стан (свіжий CRC; degraded теж валідна) — часткова робота не втрачається при таймауті/провалі rung-а.
|
|
31
|
+
- CRC у frontmatter ніколи не оновлюється без регенерації вмісту — дрейф доки не маскується.
|
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* тож rollback провального rung-а її не стирає, і великий беклог сходиться
|
|
9
9
|
* крок за кроком за кілька прогонів. Видалення сирітських док лишається під
|
|
10
10
|
* звичайним rollback (ctx.recordWrite).
|
|
11
|
+
*
|
|
12
|
+
* Інваріант: `crc-mismatch` НЕ закривається детермінованим T0-штампом CRC
|
|
13
|
+
* (fix-check.mjs для check свідомо відсутній). Свіжий CRC поверх старого тексту
|
|
14
|
+
* назавжди маскує дрейф — CRC-гейт вважає доку актуальною і вона більше ніколи
|
|
15
|
+
* не регенерується. Свіжий CRC зʼявляється лише разом зі щойно згенерованим
|
|
16
|
+
* вмістом (stampDoc усередині runGenerationBatch).
|
|
11
17
|
* @typedef {import('../../../scripts/lib/lint-surface/types.mjs').FixWorkerFn} FixWorkerFn
|
|
12
18
|
*/
|
|
13
19
|
import { join } from 'node:path'
|
|
@@ -3,13 +3,13 @@ type: JS Module
|
|
|
3
3
|
title: main.mjs
|
|
4
4
|
resource: npm/rules/doc-files/docgen-judge/main.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 47127362
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
## Огляд
|
|
11
11
|
|
|
12
|
-
Огляд: Цей файл визначає інтерфейс для механізму семантичного судження якості згенерованої технічної документації за допомогою мовної моделі. Він конфігурує параметри (`JUDGE_MODEL`, `JUDGE_ENABLED`, `JUDGE_CONFIDENCE`) та надає read-only функції для ініціації судження та інтерпретації його результатів. Додатково містить детермінований пре-гейт `detectRefusalFiller` (0 токенів): курований список refusal/чат-філер фраз моделі («Я готовий писати…», «Надайте мені
|
|
12
|
+
Огляд: Цей файл визначає інтерфейс для механізму семантичного судження якості згенерованої технічної документації за допомогою мовної моделі. Він конфігурує параметри (`JUDGE_MODEL`, `JUDGE_ENABLED`, `JUDGE_CONFIDENCE`) та надає read-only функції для ініціації судження та інтерпретації його результатів. Додатково містить детермінований пре-гейт `detectRefusalFiller` (0 токенів): курований список refusal/чат-філер фраз моделі («Я готовий писати…», «Надайте мені код…», «мені/нам потрібен сам код/файл/вміст», «щоб написати точну документацію», «I need the code»), які структурний скорер і суддя пропускали (живі кейси: score=95 на доці з суцільним філером; 2026-07-21 — «Щоб написати точну документацію, мені потрібен сам код…» злите прямо в тіло доки).
|
|
13
13
|
|
|
14
14
|
Поведінка:
|
|
15
15
|
JUDGE_MODEL — конфігурує LLM, яка використовується для семантичного судження якості документації.
|
|
@@ -35,9 +35,11 @@ const VERDICTS = new Set(['accurate', 'generic', 'inaccurate'])
|
|
|
35
35
|
* Детермінований пре-гейт (0 токенів) ПЕРЕД LLM-суддею: чат-філер/refusal локальної
|
|
36
36
|
* моделі замість документації. Живий кейс: дока зі score=95, де секції — суцільне
|
|
37
37
|
* «Я готовий писати поведінкову документацію… Надайте мені код» (gemma) — судді
|
|
38
|
-
* структура здалась валідною.
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* структура здалась валідною. Другий живий кейс (2026-07-21, робота над storybook):
|
|
39
|
+
* модель злила «Щоб написати точну документацію, мені потрібен сам код…» прямо в
|
|
40
|
+
* тіло доки — жоден зі старих патернів не збігався. Курований безпечний список
|
|
41
|
+
* (перша особа/імператив до користувача не трапляються у нормальній поведінковій
|
|
42
|
+
* доці); без `\b` — кирилиця не ASCII-`\w`, JS-межі слова не спрацьовують.
|
|
41
43
|
*/
|
|
42
44
|
const REFUSAL_FILLER_RES = [
|
|
43
45
|
/я готов(?:ий|а)/iu,
|
|
@@ -47,8 +49,12 @@ const REFUSAL_FILLER_RES = [
|
|
|
47
49
|
/не можу\s+(?:згенерувати|створити|написати)/iu,
|
|
48
50
|
/чекаю на\s+(?:код|файл|вміст)/iu,
|
|
49
51
|
/давайте почнемо/iu,
|
|
52
|
+
// живий кейс 2026-07-21: «Щоб написати точну документацію, мені потрібен сам код…»
|
|
53
|
+
/(?:мені|нам)\s+(?:потрібен|потрібно|потрібна|потрібні)\s+(?:сам(?:ий|е)?\s+)?(?:код|файл|вміст|джерел)/iu,
|
|
54
|
+
/щоб написати\s+(?:точну|повну|якісну|детальну)\s+документацію/iu,
|
|
50
55
|
/as an ai(?: language)? model/iu,
|
|
51
56
|
/i(?:['’]m| am)\s+(?:ready to|unable to)/iu,
|
|
57
|
+
/i need\s+(?:the\s+)?(?:source\s+)?(?:code|file)/iu,
|
|
52
58
|
/please provide(?: the| me)?\s+(?:code|file|source)/iu
|
|
53
59
|
]
|
|
54
60
|
|
package/rules/doc-files/main.mdc
CHANGED
|
@@ -30,8 +30,12 @@ unified lint surface (spec `docs/specs/2026-06-29-unified-lint-surface.md`) і
|
|
|
30
30
|
|
|
31
31
|
Алгоритм детекту (кандидати, ignore-дерево, CRC, реверс-мапінг доки→джерело) — у
|
|
32
32
|
`docgen-scan/main.mjs` / `docgen-crc/main.mjs` / `docgen-ignore/main.mjs`, детектор — у
|
|
33
|
-
`check/main.mjs` (`lint(ctx)`), fix —
|
|
34
|
-
|
|
33
|
+
`check/main.mjs` (`lint(ctx)`), fix — лише `check/fix-worker.mjs` (LLM-регенерація); тут —
|
|
34
|
+
лише людинозрозумілий контракт, без дублювання логіки.
|
|
35
|
+
|
|
36
|
+
T0-штампу CRC для `crc-mismatch` **немає навмисно**: свіжий CRC поверх старого тексту
|
|
37
|
+
назавжди маскує дрейф (CRC-гейт вважає доку актуальною і вона більше не регенерується).
|
|
38
|
+
Свіжий CRC пише лише генерація разом із новим вмістом.
|
|
35
39
|
|
|
36
40
|
## Hook'и
|
|
37
41
|
|
|
@@ -3,7 +3,7 @@ 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:
|
|
6
|
+
crc: a0f45fc3
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
issues: judge:inaccurate:0.98
|
|
@@ -40,3 +40,5 @@ detectAll виконує прохід лінтера у режим детекц
|
|
|
40
40
|
**Multi-dir (плагіни):** `effectiveRulesDirs` додає rules-каталоги плагінів з `.n-rules.json` (hot-path: без install, quiet); `readLintConcernsByRuleMulti` зливає концерни за іменем (перший власник виграє) — плагін може додавати концерни до правила ядра (mixin).
|
|
41
41
|
|
|
42
42
|
**Capability-гейт:** `filterByCapabilities` відкидає концерни з незадоволеним `requires.capability` (capabilities надають встановлені плагіни через маніфест `n-rules.capabilities`; явний `opts.capabilities` у тестах перекриває резолв).
|
|
43
|
+
|
|
44
|
+
**Warning про rule-id без concern-ів:** якщо rule-id з `.n-rules.json#rules` не має жодного concern-а серед усіх `rulesDirs` (ядро + плагіни) — `console.error` попереджає про можливий дрейф конфігу (типово: правило переїхало в плагін, якого консюмер не підключив у `plugins[]`). Rule-id з існуючим каталогом, але без `concern.json` (документаційні правила на кшталт `feedback`), warning не тригерить.
|
|
@@ -152,15 +152,70 @@ async function readLintConcernsByRuleMulti(rulesDirs) {
|
|
|
152
152
|
return merged
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Імена ВСІХ каталогів верхнього рівня під `rulesDirs` — незалежно від того, чи знайшлись
|
|
157
|
+
* у них concern-и. Потрібно, щоб відрізнити «каталог є, просто без lint-поверхні» (легітимно
|
|
158
|
+
* для суто-документаційних правил на кшталт `feedback`/`local-ai`) від «каталогу немає
|
|
159
|
+
* взагалі ні в ядрі, ні в жодному підключеному плагіні» (ознака дрейфу конфігу — типово
|
|
160
|
+
* правило переїхало в плагін, якого консюмер не підключив).
|
|
161
|
+
* @param {string[]} rulesDirs rules-каталоги (ядро + плагіни).
|
|
162
|
+
* @returns {Promise<Set<string>>} унікальні імена каталогів-правил.
|
|
163
|
+
*/
|
|
164
|
+
async function discoverAllRuleDirNames(rulesDirs) {
|
|
165
|
+
const { readdir } = await import('node:fs/promises')
|
|
166
|
+
/** @type {Set<string>} */
|
|
167
|
+
const names = new Set()
|
|
168
|
+
for (const dir of rulesDirs) {
|
|
169
|
+
let entries
|
|
170
|
+
try {
|
|
171
|
+
entries = await readdir(dir, { withFileTypes: true })
|
|
172
|
+
} catch {
|
|
173
|
+
continue
|
|
174
|
+
}
|
|
175
|
+
for (const e of entries) {
|
|
176
|
+
if (e.isDirectory() && !e.name.startsWith('.')) names.add(e.name)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return names
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Попереджає про rule-id з `.n-rules.json#rules`, яких немає ЖОДНИМ каталогом ні в ядрі, ні
|
|
184
|
+
* в підключених плагінах (не плутати з «каталог є, але без concern-ів» — легітимний випадок
|
|
185
|
+
* для суто-документаційних правил). Типова причина: правило переїхало в окремий плагін
|
|
186
|
+
* (напр. `js` → `@7n/rules-lang-js` з фази 5c), а `plugins[]` консюмера про це не знає —
|
|
187
|
+
* тоді перевірки для цього правила мовчки НЕ виконуються (0 знайдених concern-ів виглядає
|
|
188
|
+
* як «усе чисто», хоча насправді нічого не перевірялось).
|
|
189
|
+
* @param {Record<string, ConcernMeta[]>} byRule concerns згруповані за rule-id (з усіх rulesDirs).
|
|
190
|
+
* @param {import('../read-n-rules-config-lite.mjs').LiteConfig} config розпарсений .n-rules.json.
|
|
191
|
+
* @param {string[]} rulesDirs rules-каталоги (ядро + плагіни), для перевірки «каталог є, але порожній».
|
|
192
|
+
* @returns {Promise<void>}
|
|
193
|
+
*/
|
|
194
|
+
async function warnAboutRulesWithoutConcerns(byRule, config, rulesDirs) {
|
|
195
|
+
const missing = config.rules.filter(id => !(id in byRule))
|
|
196
|
+
if (missing.length === 0) return
|
|
197
|
+
const allDirNames = await discoverAllRuleDirNames(rulesDirs)
|
|
198
|
+
for (const ruleId of missing) {
|
|
199
|
+
if (allDirNames.has(ruleId)) continue // каталог є, просто без lint-поверхні — легітимно
|
|
200
|
+
console.error(
|
|
201
|
+
`⚠️ .n-rules.json: правило "${ruleId}" не знайдено НІ В ОДНОМУ з rulesDirs (ні в ядрі, ні в ` +
|
|
202
|
+
`підключених плагінах "plugins") — перевірки для нього НЕ виконуються. Якщо правило нещодавно ` +
|
|
203
|
+
`переїхало в окремий плагін, додай відповідний пакет у "plugins" (і в devDependencies).`
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
155
208
|
/**
|
|
156
209
|
* Активні rule-id з `.n-rules.json` (для delta/full режимів).
|
|
157
210
|
* @param {Record<string, ConcernMeta[]>} byRule concerns згруповані за rule-id.
|
|
158
211
|
* @param {string} cwd робоча директорія прогону.
|
|
212
|
+
* @param {string[]} rulesDirs rules-каталоги (ядро + плагіни) — для warning про відсутні правила.
|
|
159
213
|
* @returns {Promise<string[]>} перелік активних rule-id.
|
|
160
214
|
*/
|
|
161
|
-
async function enabledRuleIds(byRule, cwd) {
|
|
215
|
+
async function enabledRuleIds(byRule, cwd, rulesDirs) {
|
|
162
216
|
const config = await readNRulesConfigLite(cwd)
|
|
163
217
|
if (!config.exists) return []
|
|
218
|
+
await warnAboutRulesWithoutConcerns(byRule, config, rulesDirs)
|
|
164
219
|
return Object.keys(byRule).filter(id => isRuleEnabled(config, id))
|
|
165
220
|
}
|
|
166
221
|
|
|
@@ -189,7 +244,8 @@ function sortEntries(entries) {
|
|
|
189
244
|
* @returns {Promise<PlanItem[]>} впорядкований план прогону.
|
|
190
245
|
*/
|
|
191
246
|
export async function buildDetectPlan(opts) {
|
|
192
|
-
const
|
|
247
|
+
const rulesDirs = await effectiveRulesDirs(opts)
|
|
248
|
+
const byRule = await filterByCapabilities(await readLintConcernsByRuleMulti(rulesDirs), opts)
|
|
193
249
|
return buildPlan({
|
|
194
250
|
byRule,
|
|
195
251
|
full: opts.full === true,
|
|
@@ -198,7 +254,8 @@ export async function buildDetectPlan(opts) {
|
|
|
198
254
|
pathMode: opts.pathMode === true,
|
|
199
255
|
repoWide: opts.repoWide === true,
|
|
200
256
|
baseRef: typeof opts.baseRef === 'string' ? opts.baseRef : null,
|
|
201
|
-
cwd: opts.cwd
|
|
257
|
+
cwd: opts.cwd,
|
|
258
|
+
rulesDirs
|
|
202
259
|
})
|
|
203
260
|
}
|
|
204
261
|
|
|
@@ -209,8 +266,9 @@ export async function buildDetectPlan(opts) {
|
|
|
209
266
|
* @returns {Promise<{ byRule: Record<string, ConcernMeta[]>, enabledSet: Set<string> }>} concerns і активні правила.
|
|
210
267
|
*/
|
|
211
268
|
export async function loadEnabledLintRules(opts) {
|
|
212
|
-
const
|
|
213
|
-
const
|
|
269
|
+
const rulesDirs = await effectiveRulesDirs(opts)
|
|
270
|
+
const byRule = await filterByCapabilities(await readLintConcernsByRuleMulti(rulesDirs), opts)
|
|
271
|
+
const enabledSet = new Set(await enabledRuleIds(byRule, opts.cwd, rulesDirs))
|
|
214
272
|
return { byRule, enabledSet }
|
|
215
273
|
}
|
|
216
274
|
|
|
@@ -378,6 +436,7 @@ export function computeActiveDomains(byRule, enabledSet, changed) {
|
|
|
378
436
|
* @param {boolean} [args.repoWide] `--repo-wide`: лише full-scope concerns, whole-repo.
|
|
379
437
|
* @param {string|null} [args.baseRef] явна база дельти (`--base <ref>`) замість каскаду main→origin/main.
|
|
380
438
|
* @param {string} args.cwd робоча директорія прогону.
|
|
439
|
+
* @param {string[]} [args.rulesDirs] rules-каталоги (ядро + плагіни) — для warning про відсутні правила.
|
|
381
440
|
* @returns {Promise<PlanItem[]>} впорядкований план прогону.
|
|
382
441
|
*/
|
|
383
442
|
async function buildPlan({
|
|
@@ -388,14 +447,15 @@ async function buildPlan({
|
|
|
388
447
|
pathMode = false,
|
|
389
448
|
repoWide = false,
|
|
390
449
|
baseRef = null,
|
|
391
|
-
cwd
|
|
450
|
+
cwd,
|
|
451
|
+
rulesDirs = []
|
|
392
452
|
}) {
|
|
393
453
|
// scoped + --path: per-file concerns названих правил × перетин path ∩ дельта
|
|
394
454
|
if (rules.length > 0 && explicitFiles !== null) return buildScopedDeltaPlan(byRule, rules, explicitFiles)
|
|
395
455
|
// scoped: усі lint-concerns названих правил, whole-repo
|
|
396
456
|
if (rules.length > 0) return buildScopedPlan(byRule, rules)
|
|
397
457
|
|
|
398
|
-
const enabled = await enabledRuleIds(byRule, cwd)
|
|
458
|
+
const enabled = await enabledRuleIds(byRule, cwd, rulesDirs)
|
|
399
459
|
const enabledSet = new Set(enabled)
|
|
400
460
|
|
|
401
461
|
// repo-wide: лише full-scope concerns (окремий CI-workflow, не гейтить деплой)
|
|
@@ -557,7 +617,8 @@ export async function detectAll(opts) {
|
|
|
557
617
|
const verbose = opts.verbose === true
|
|
558
618
|
const baseLog = opts.log ?? (s => process.stdout.write(s))
|
|
559
619
|
|
|
560
|
-
const
|
|
620
|
+
const rulesDirs = await effectiveRulesDirs(opts)
|
|
621
|
+
const byRule = await filterByCapabilities(await readLintConcernsByRuleMulti(rulesDirs), opts)
|
|
561
622
|
const plan = await buildPlan({
|
|
562
623
|
byRule,
|
|
563
624
|
full,
|
|
@@ -566,7 +627,8 @@ export async function detectAll(opts) {
|
|
|
566
627
|
pathMode: opts.pathMode === true,
|
|
567
628
|
repoWide: opts.repoWide === true,
|
|
568
629
|
baseRef: typeof opts.baseRef === 'string' ? opts.baseRef : null,
|
|
569
|
-
cwd
|
|
630
|
+
cwd,
|
|
631
|
+
rulesDirs
|
|
570
632
|
})
|
|
571
633
|
|
|
572
634
|
// Detect-only бар — ЛИШЕ в TTY (без тикера «виправлено»). У не-TTY (hooks, CI-gate,
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: JS Module
|
|
3
|
-
title: fix-check.mjs
|
|
4
|
-
resource: npm/rules/doc-files/check/fix-check.mjs
|
|
5
|
-
docgen:
|
|
6
|
-
crc: 9a1d9107
|
|
7
|
-
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
tier: local-min
|
|
9
|
-
score: 100
|
|
10
|
-
issues: judge:inaccurate:0.97
|
|
11
|
-
judgeModel: openai-codex/gpt-5.4-mini
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## Огляд
|
|
15
|
-
|
|
16
|
-
Файл реалізує детерміноване оновлення CRC-штампів у документації, що спрацьовує після виявлення розбіжності `crc-mismatch`. Механізм автоматично оновлює метадані документації, щоб фіксувати відповідність між зміненими джерелами та вже існуючим, незміненим контентом. Це забезпечує точну валідацію метаданих системи.
|
|
17
|
-
|
|
18
|
-
## Поведінка
|
|
19
|
-
|
|
20
|
-
1. Перевіряється наявність вказівок на розбіжність CRC у документації.
|
|
21
|
-
2. Для документації, яка відповідає критеріям розбіжності CRC, але є цілісною, виконується детерміноване оновлення CRC в метаданих.
|
|
22
|
-
3. Операція включає аналіз якості документації, ініційована з використанням відповідних моделей.
|
|
23
|
-
4. Оновлена документація записується у файл, ідентифікований шляхом до документації, що відповідає розбіжності.
|
|
24
|
-
5. Повертається булеве значення, що вказує на те, чи було внесено зміни у документацію.
|
|
25
|
-
|
|
26
|
-
## Гарантії поведінки
|
|
27
|
-
|
|
28
|
-
- (специфічних машинно-виведених гарантій немає)
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* T0-autofix doc-files/check — детермінований CRC-stamp для `crc-mismatch` доків
|
|
3
|
-
* (джерело змінилось, але дока актуальна → лише оновити CRC у frontmatter, без LLM).
|
|
4
|
-
* `missing`/`degraded`/`orphaned-doc` лишаються worker-у (генерація/очистка).
|
|
5
|
-
*
|
|
6
|
-
* Unified lint surface: structured violations; запускається ПЕРЕД fix-worker-ом.
|
|
7
|
-
*/
|
|
8
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'
|
|
9
|
-
import { join, dirname } from 'node:path'
|
|
10
|
-
|
|
11
|
-
/** @type {import('../../../scripts/lib/lint-surface/types.mjs').T0Pattern[]} */
|
|
12
|
-
export const patterns = [
|
|
13
|
-
{
|
|
14
|
-
id: 'doc-files-stamp-crc',
|
|
15
|
-
test: violations => violations.some(v => v.reason === 'crc-mismatch'),
|
|
16
|
-
apply: async (violations, ctx) => {
|
|
17
|
-
const { scanForDocFiles } = await import('../docgen-scan/main.mjs')
|
|
18
|
-
const { crc32, readDocModel, readDocQuality, stampDoc } = await import('../docgen-crc/main.mjs')
|
|
19
|
-
const { cwd } = ctx
|
|
20
|
-
/** @type {string[]} */
|
|
21
|
-
const touchedFiles = []
|
|
22
|
-
|
|
23
|
-
const staleFiles = scanForDocFiles(cwd).filter(f => f.stale && f.reason === 'crc-mismatch')
|
|
24
|
-
for (const file of staleFiles) {
|
|
25
|
-
const sourceAbs = join(cwd, file.sourcePath)
|
|
26
|
-
const docAbs = join(cwd, file.docPath)
|
|
27
|
-
if (!existsSync(docAbs)) continue // missing → worker, не T0
|
|
28
|
-
const { score, issues, judgeModel } = readDocQuality(docAbs)
|
|
29
|
-
const quality = score === null ? null : { score, issues, judge: judgeModel ? { model: judgeModel } : undefined }
|
|
30
|
-
const crc = crc32(readFileSync(sourceAbs))
|
|
31
|
-
ctx.recordWrite?.(docAbs)
|
|
32
|
-
mkdirSync(dirname(docAbs), { recursive: true })
|
|
33
|
-
writeFileSync(
|
|
34
|
-
docAbs,
|
|
35
|
-
stampDoc(readFileSync(docAbs, 'utf8'), file.sourcePath, crc, quality, readDocModel(docAbs))
|
|
36
|
-
)
|
|
37
|
-
touchedFiles.push(docAbs)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return touchedFiles.length > 0
|
|
41
|
-
? { touchedFiles, message: `stamped CRC: ${touchedFiles.length} доки(ів)` }
|
|
42
|
-
: { touchedFiles: [] }
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
]
|