@7n/rules 1.40.0 → 1.41.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/bin/n-rules.js +22 -3
- 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/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.41.0] - 2026-07-22
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- doc-files: прибрано безумовний T0 CRC-штамп для crc-mismatch (fix-check.mjs) — свіжий CRC поверх застарілого тексту назавжди маскував дрейф доки; тепер застаріла дока завжди регенерується fix-worker-ом (docgen). Guardrail: detectRefusalFiller ловить нові живі refusal-фрази локальної моделі («мені потрібен сам код», «щоб написати точну документацію», «I need the code»)
|
|
8
|
+
|
|
9
|
+
## [1.40.1] - 2026-07-22
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- `lint --full`: загублений `await` перед `ensureRunningInWorktree` ламав кожен прогін (у т.ч. зсередини `.worktrees/`) з `TypeError [ERR_INVALID_ARG_TYPE]`; заодно — guard проти видалення auto-created worktree при частковому провалі перенесення змін назад, і stack trace для programmer-помилок у top-level catch
|
|
14
|
+
|
|
3
15
|
## [1.40.0] - 2026-07-22
|
|
4
16
|
|
|
5
17
|
### Changed
|
package/bin/n-rules.js
CHANGED
|
@@ -1848,7 +1848,7 @@ try {
|
|
|
1848
1848
|
// дереві задачі, worktree-ізоляція зламала б саму суть дельти) — пропускаємо.
|
|
1849
1849
|
const needsWorktreeIsolation = full && !noFix
|
|
1850
1850
|
const worktree = needsWorktreeIsolation
|
|
1851
|
-
? ensureRunningInWorktree(cwdArg, spawnSync, line => console.log(line), {
|
|
1851
|
+
? await ensureRunningInWorktree(cwdArg, spawnSync, line => console.log(line), {
|
|
1852
1852
|
suffix: 'lint',
|
|
1853
1853
|
description: 'n-rules lint --full: worktree-only full-repo run'
|
|
1854
1854
|
})
|
|
@@ -1881,14 +1881,26 @@ try {
|
|
|
1881
1881
|
} finally {
|
|
1882
1882
|
// Лише для АВТОстворених worktree (лінт уже сидів у своєму — не наш, не чіпаємо).
|
|
1883
1883
|
if (worktree.autoCreated) {
|
|
1884
|
+
let bringBackFailed = true
|
|
1884
1885
|
try {
|
|
1885
|
-
await bringChangesBackToOriginal(runCwd, cwdArg, spawnSync, line =>
|
|
1886
|
+
const bringBackResult = await bringChangesBackToOriginal(runCwd, cwdArg, spawnSync, line =>
|
|
1887
|
+
console.log(line)
|
|
1888
|
+
)
|
|
1889
|
+
bringBackFailed = bringBackResult.failed
|
|
1886
1890
|
} catch (error) {
|
|
1887
1891
|
console.log(
|
|
1888
1892
|
`⚠️ Перенесення змін назад провалилось: ${error instanceof Error ? error.message : String(error)}`
|
|
1889
1893
|
)
|
|
1890
1894
|
}
|
|
1891
|
-
|
|
1895
|
+
// Прибираємо worktree лише якщо перенесення точно вдалось — інакше
|
|
1896
|
+
// не перенесені зміни згорять разом з деревом.
|
|
1897
|
+
if (bringBackFailed) {
|
|
1898
|
+
console.log(
|
|
1899
|
+
`⚠️ Перенесення назад не підтверджено — worktree "${worktree.branchArg}" лишається для ручного розбору.`
|
|
1900
|
+
)
|
|
1901
|
+
} else {
|
|
1902
|
+
removeAutoCreatedWorktree(worktree.branchArg, cwdArg, spawnSync, line => console.log(line))
|
|
1903
|
+
}
|
|
1892
1904
|
}
|
|
1893
1905
|
}
|
|
1894
1906
|
|
|
@@ -1964,8 +1976,15 @@ try {
|
|
|
1964
1976
|
}
|
|
1965
1977
|
}
|
|
1966
1978
|
} catch (error) {
|
|
1979
|
+
// TypeError/RangeError/ReferenceError сигналять баг у самому коді (не навмисне
|
|
1980
|
+
// user-facing повідомлення) — друкуємо stack одразу, інакше діагностика вимагає
|
|
1981
|
+
// патчити node_modules вручну (як під час діагностики цього ж класу вад).
|
|
1982
|
+
const isProgrammerError = error instanceof TypeError || error instanceof RangeError || error instanceof ReferenceError
|
|
1967
1983
|
if (error instanceof ReexecHandoff) {
|
|
1968
1984
|
process.exitCode = error.code
|
|
1985
|
+
} else if (isProgrammerError && error.stack) {
|
|
1986
|
+
console.error(error.stack)
|
|
1987
|
+
process.exitCode = 1
|
|
1969
1988
|
} else if (error instanceof Error && error.message) {
|
|
1970
1989
|
console.error(error.message)
|
|
1971
1990
|
process.exitCode = 1
|
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
|
|
|
@@ -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
|
-
]
|