@7n/rules 1.10.0 → 1.11.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 +10 -0
- package/package.json +1 -1
- package/rules/js/eslint/docs/fix-worker.md +30 -0
- package/rules/js/eslint/docs/index.md +11 -0
- package/rules/js/eslint/fix-worker.mjs +99 -0
- package/rules/js/knip/concern.json +1 -0
- package/rules/text/markdownlint/docs/main.md +11 -22
- package/rules/text/markdownlint/main.mjs +12 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.11.0] - 2026-07-17
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- js/eslint: fix-worker.mjs з per-file циклом замість одного великого промпту на весь concern — усунення 100% timeout на драбині, виміряно на реальних lint-прогонах
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- text/markdownlint: включати причину провалу markdownlint-cli2 у violation-повідомлення; js/knip: fixability=structural (LLM-ладдер завжди приречений на timeout)
|
|
12
|
+
|
|
3
13
|
## [1.10.0] - 2026-07-17
|
|
4
14
|
|
|
5
15
|
### Added
|
package/package.json
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: fix-worker.mjs
|
|
4
|
+
resource: npm/rules/js/eslint/fix-worker.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: 1eb9c7fc
|
|
7
|
+
model: manual
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Огляд
|
|
11
|
+
|
|
12
|
+
Custom fix-worker `js/eslint` (перекриває дефолтний `default-worker.mjs`): замість одного `runAgentFix`-виклику з усіма порушеннями з усіх файлів concern-а — окрема агентна сесія на кожен файл, у межах спільного дедлайну rung-а. Мотивація — виміряно на реальних lint-прогонах: одна сесія, що жонглює кількома файлами одразу, стабільно впирається у timeout на всіх 4 rung-ах драбини (local-min→cloud-avg), навіть при малому обсязі порушень; та сама модель, scoped на один файл, укладається в бюджет і закриває більшість порушень.
|
|
13
|
+
|
|
14
|
+
## Поведінка
|
|
15
|
+
|
|
16
|
+
1. Групує вхідні `violations` за `file`; порушення без `file` ігноруються.
|
|
17
|
+
2. Дедлайн — `DEADLINE_FRACTION` (0.8) від `ctx.timeoutMs`; цикл перевіряє дедлайн ПЕРЕД стартом кожного файлу і не починає новий, якщо час вичерпано.
|
|
18
|
+
3. Кожен файл отримує РЕШТУ бюджету до дедлайну (не фіксований поділ `timeoutMs / files.length`) — перший (часто найважчий) файл отримує найбільше часу.
|
|
19
|
+
4. На кожен файл — окремий `runAgentFix` із `targetFiles: [file]` і власним `verify` (`verifyFile`): item-scoped canonical re-detect лише цього файлу, не всього concern-а — інакше evidence-гейт хибно вважав би файл незакритим через порушення в ІНШИХ файлах.
|
|
20
|
+
5. Один файл, що завершився з `error`, не обриває цикл — пропускається, наступні файли все одно обробляються в межах дедлайну.
|
|
21
|
+
6. Повертає лише `touchedFiles` з успішних (`!error`) викликів; success rung-а все одно визначає whole-concern canonical re-detect runner-а (`runRung`), не цей worker.
|
|
22
|
+
|
|
23
|
+
## Публічний API
|
|
24
|
+
|
|
25
|
+
- `fixWorker(violations, ctx)` — контракт `FixWorkerFn`; резолвиться автоматично замість `default-worker.mjs`, бо лежить як `fix-worker.mjs` поруч із `main.mjs` concern-а.
|
|
26
|
+
|
|
27
|
+
## Гарантії поведінки
|
|
28
|
+
|
|
29
|
+
- Пише лише у файли з переданих `violations` (`targetFiles: [file]` на кожен виклик) — той самий semantic-collateral guard, що й у дефолтного worker-а.
|
|
30
|
+
- `recordWrite` (не durable) на кожен файл: rollback-контракт незмінний — якщо після worker-а в concern-і лишилось хоч одне порушення будь-де, `runRung` відкочує ВСІ правки цього rung-а, включно з уже полагодженими файлами.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Directory Index
|
|
3
|
+
title: npm/rules/js/eslint
|
|
4
|
+
resource: npm/rules/js/eslint/
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
| Файл | Тип |
|
|
8
|
+
| ------------------------------- | --------- |
|
|
9
|
+
| [fix-eslint.mjs](fix-eslint.md) | JS Module |
|
|
10
|
+
| [fix-worker.mjs](fix-worker.md) | JS Module |
|
|
11
|
+
| [main.mjs](main.md) | JS Module |
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fix-worker для `js/eslint`: окрема агентна сесія НА ФАЙЛ замість одного великого промпту
|
|
3
|
+
* на всі файли concern-а одразу. Дефолтний worker (`default-worker.mjs`) шле ВСІ порушення
|
|
4
|
+
* з УСІХ файлів в один `runAgentFix`-виклик — на реальних lint-прогонах (tauri-components)
|
|
5
|
+
* це давало 100% timeout на всіх 4 rung-ах драбини (local-min→cloud-avg), навіть коли
|
|
6
|
+
* порушень було лише 20-90 у 1-3 файлах: одна сесія жонглює кількома файлами одразу і не
|
|
7
|
+
* встигає в rung-таймаут. Виміряно (2026-07-17, ручний timing-експеримент поза pipeline):
|
|
8
|
+
* одна сесія, scoped на ОДИН найважчий файл (21 порушення), закрила 76% (16/21) за 91с із
|
|
9
|
+
* бюджету 120с (cloud-min) — per-file scoping вкладається, mega-prompt — ні.
|
|
10
|
+
*
|
|
11
|
+
* Rollback-контракт незмінний: `recordWrite` (не durable) на кожен файл — правки наявного
|
|
12
|
+
* стороннього коду. Якщо після worker-а `runRung`-ів canonical re-detect (whole-concern)
|
|
13
|
+
* знайде хоч одне порушення будь-де, увесь rung однаково відкотиться (S1) — той самий
|
|
14
|
+
* контракт, що й у дефолтного worker-а; батчинг підвищує ймовірність, що ЦЕЙ rung дійде
|
|
15
|
+
* до 0 порушень, а не замінює rollback-семантику.
|
|
16
|
+
*
|
|
17
|
+
* Дедлайн (`DEADLINE_FRACTION` від `ctx.timeoutMs`, той самий підхід, що й
|
|
18
|
+
* `doc-files/check/fix-worker.mjs`): цикл не стартує наступний файл, якщо дедлайн
|
|
19
|
+
* настав — worker повертає часткову роботу штатно, замість фонової сесії, що триває
|
|
20
|
+
* поверх backstop ×1.25 runner-а. Кожен файл отримує РЕШТУ бюджету до дедлайну (не
|
|
21
|
+
* фіксований `timeoutMs / files.length`) — перший (часто найважчий) файл отримує
|
|
22
|
+
* найбільше часу, а не штучно урізаний рівний шматок.
|
|
23
|
+
* @typedef {import('../../../scripts/lib/lint-surface/types.mjs').FixWorkerFn} FixWorkerFn
|
|
24
|
+
*/
|
|
25
|
+
import { resolve } from 'node:path'
|
|
26
|
+
|
|
27
|
+
import { anchoredEnabled } from '../../../scripts/lib/lint-surface/default-worker.mjs'
|
|
28
|
+
import { renderViolations } from '../../../scripts/lib/lint-surface/render.mjs'
|
|
29
|
+
import { lint } from './main.mjs'
|
|
30
|
+
|
|
31
|
+
/** Частка ctx.timeoutMs, після якої цикл не стартує наступний файл (запас до backstop ×1.25). */
|
|
32
|
+
const DEADLINE_FRACTION = 0.8
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Item-scoped (один файл) canonical re-detect для evidence-гейта `runAgentFix` —
|
|
36
|
+
* НЕ те саме, що whole-concern `ctx.verify` з `runRung` (той перевірив би ВСІ файли
|
|
37
|
+
* concern-а й дав би хибний "не готово" навіть коли саме ЦЕЙ файл уже чистий).
|
|
38
|
+
* @param {string} cwd абсолютний корінь проєкту
|
|
39
|
+
* @param {string} ruleId id правила
|
|
40
|
+
* @param {string} concernId id concern-а
|
|
41
|
+
* @param {string} file posix-relative шлях файлу, що перевіряється
|
|
42
|
+
* @returns {Promise<{ ok: boolean, output: string }>} вердикт verify-петлі
|
|
43
|
+
*/
|
|
44
|
+
async function verifyFile(cwd, ruleId, concernId, file) {
|
|
45
|
+
const { violations: after } = await lint({ cwd, ruleId, concernId, files: [file] })
|
|
46
|
+
const stamped = after.map(v => ({ ...v, ruleId, concernId }))
|
|
47
|
+
return { ok: stamped.length === 0, output: renderViolations(stamped) }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** @type {FixWorkerFn} */
|
|
51
|
+
export async function fixWorker(violations, ctx) {
|
|
52
|
+
// lazy import — тримає detect-шлях вільним від pi/oxc (read-only --no-fix не вантажить їх).
|
|
53
|
+
const [{ runAgentFix }, { isLocalModel }, { extractContext }] = await Promise.all([
|
|
54
|
+
import('@7n/llm-lib/agent-fix'),
|
|
55
|
+
import('@7n/llm-lib/model-tiers'),
|
|
56
|
+
import('../../../scripts/utils/ast-extract.mjs')
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
/** @type {Map<string, import('../../../scripts/lib/lint-surface/types.mjs').LintViolation[]>} */
|
|
60
|
+
const byFile = new Map()
|
|
61
|
+
for (const v of violations) {
|
|
62
|
+
if (!v.file) continue
|
|
63
|
+
const arr = byFile.get(v.file)
|
|
64
|
+
if (arr) arr.push(v)
|
|
65
|
+
else byFile.set(v.file, [v])
|
|
66
|
+
}
|
|
67
|
+
const files = byFile.keys().toArray()
|
|
68
|
+
if (files.length === 0) return { touchedFiles: [] }
|
|
69
|
+
|
|
70
|
+
const deadlineAt = ctx.timeoutMs ? Date.now() + Math.round(ctx.timeoutMs * DEADLINE_FRACTION) : null
|
|
71
|
+
const anchoredEdits = anchoredEnabled(ctx.model, isLocalModel)
|
|
72
|
+
|
|
73
|
+
const touchedFiles = []
|
|
74
|
+
for (const file of files) {
|
|
75
|
+
if (deadlineAt && Date.now() >= deadlineAt) break
|
|
76
|
+
const callTimeoutMs = deadlineAt ? Math.max(1000, deadlineAt - Date.now()) : ctx.timeoutMs
|
|
77
|
+
|
|
78
|
+
const res = await runAgentFix(ctx.ruleId, renderViolations(byFile.get(file)), ctx.cwd, {
|
|
79
|
+
model: ctx.model,
|
|
80
|
+
tier: ctx.tier,
|
|
81
|
+
timeoutMs: callTimeoutMs,
|
|
82
|
+
feedback: ctx.feedback ?? null,
|
|
83
|
+
caller: `fix:${ctx.ruleId}/${ctx.concernId}:${ctx.tier}:${file}`,
|
|
84
|
+
recordWrite: ctx.recordWrite,
|
|
85
|
+
chain: ctx.chain ?? null,
|
|
86
|
+
targetFiles: [file],
|
|
87
|
+
verify: () => verifyFile(ctx.cwd, ctx.ruleId, ctx.concernId, file),
|
|
88
|
+
verifyMax: ctx.verifyMax,
|
|
89
|
+
anchoredEdits,
|
|
90
|
+
deps: { astContext: p => extractContext(resolve(ctx.cwd, p)) }
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// Один файл не впорався — не кидаємо, пробуємо решту в межах дедлайну; whole-concern
|
|
94
|
+
// canonical re-detect runner-а (не цей worker) визначить, чи rung закрито.
|
|
95
|
+
if (!res.error) touchedFiles.push(...(res.touchedFiles ?? []))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { touchedFiles }
|
|
99
|
+
}
|
|
@@ -3,38 +3,27 @@ type: JS Module
|
|
|
3
3
|
title: main.mjs
|
|
4
4
|
resource: npm/rules/text/markdownlint/main.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
7
|
-
model:
|
|
8
|
-
tier: cloud-avg
|
|
9
|
-
score: 95
|
|
10
|
-
issues: anchor-miss:(text.mdc),judge:inaccurate:0.98
|
|
11
|
-
judgeModel: openai-codex/gpt-5.4-mini
|
|
6
|
+
crc: 6aec4713
|
|
7
|
+
model: manual
|
|
12
8
|
---
|
|
13
9
|
|
|
14
10
|
## Огляд
|
|
15
11
|
|
|
16
|
-
|
|
12
|
+
Multi-surface detector `text/markdownlint`: `policy` перевіряє наявність `.markdownlint-cli2.jsonc`, `lint` запускає сам `markdownlint-cli2` (delta — по `ctx.files`, full — за glob `**/*.md`/`**/*.mdc`) і повертає обидва результати в одному `LintResult`.
|
|
17
13
|
|
|
18
14
|
## Поведінка
|
|
19
15
|
|
|
20
|
-
1. `
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
4. `lint` не запускає markdownlint, якщо серед цільових файлів немає Markdown-документів, і повертає лише вже зібрані порушення політики.
|
|
27
|
-
|
|
28
|
-
5. `lint` приглушує прямий вивід markdownlint, щоб detector повертав уніфікований результат lint-перевірки без шуму в консолі.
|
|
29
|
-
|
|
30
|
-
6. `lint` додає порушення з маркером ``, якщо markdownlint знаходить проблеми у Markdown або MDC-файлах.
|
|
31
|
-
|
|
32
|
-
7. `lint` повертає спільний список порушень для policy- та lint-поверхонь, щоб markdown-концерн поводився як один цілісний detector.
|
|
16
|
+
1. Policy-перевірка (`evaluatePolicyConcern`) додає порушення, якщо `.markdownlint-cli2.jsonc` відсутній.
|
|
17
|
+
2. Якщо серед цільових файлів немає Markdown/MDC — `markdownlint-cli2` не запускається, повертаються лише policy-порушення.
|
|
18
|
+
3. `logMessage` (banner, `Finding:`/`Found:`/`Linting:`/`Summary:` прогрес-текст) — no-op, не деталь порушення.
|
|
19
|
+
4. `logError` (один готовий рядок на порушення від дефолтного форматера markdownlint-cli2: `<file>:<line>:<col> <rule> <опис> [<деталь>]`) — накопичується в масив і вбудовується у violation-повідомлення. Без цього LLM fix-worker (і non-verbose підсумок) бачив лише голе "markdownlint знайшов порушення", без файлу/рядка/правила.
|
|
20
|
+
5. Ненульовий exit-код `markdownlint-cli2` → одне порушення `reason: 'markdownlint'` з накопиченою деталлю.
|
|
33
21
|
|
|
34
22
|
## Публічний API
|
|
35
23
|
|
|
36
|
-
- lint —
|
|
24
|
+
- `lint(ctx)` — detector-контракт unified lint surface; повертає `{ violations }` (policy + lint-порушення разом).
|
|
37
25
|
|
|
38
26
|
## Гарантії поведінки
|
|
39
27
|
|
|
40
|
-
- Read-only: не
|
|
28
|
+
- Read-only: не пише `.markdownlint-cli2.jsonc` сам — генерація конфігу окремим T0 (не в цьому detector-і).
|
|
29
|
+
- `logError`-деталь — єдине джерело причини провалу; текстові евристики за кодом виходу не використовуються.
|
|
@@ -32,22 +32,30 @@ export async function lint(ctx) {
|
|
|
32
32
|
const targets = ctx.files === undefined ? DEFAULT_MD_GLOBS : ctx.files.filter(f => MD_EXT_RE.test(f))
|
|
33
33
|
if (targets.length === 0) return { violations }
|
|
34
34
|
|
|
35
|
+
// logError отримує один готовий рядок на порушення через default output formatter
|
|
36
|
+
// markdownlint-cli2 ("<file>:<line>:<col> <rule> <опис> [<деталь>]") — раніше глушився,
|
|
37
|
+
// detector повертав лише голе "щось не пройшло" без файлу/правила/причини, тож LLM
|
|
38
|
+
// fix-worker (як і non-verbose підсумок) не мав інформації, що саме виправляти
|
|
39
|
+
// (той самий патерн, що й text/run-v8r до фіксу). logMessage лишається no-op —
|
|
40
|
+
// banner/Finding/Found/Linting/Summary прогрес-текст, не деталь порушення.
|
|
41
|
+
const errorLines = []
|
|
35
42
|
const code = await markdownlintCli2({
|
|
36
43
|
directory: ctx.cwd,
|
|
37
44
|
argv: targets,
|
|
38
45
|
logMessage: () => {
|
|
39
|
-
//
|
|
46
|
+
// прогрес-статус markdownlint-cli2 (banner, Finding/Found/Linting/Summary) — не деталь
|
|
40
47
|
},
|
|
41
|
-
logError:
|
|
42
|
-
|
|
48
|
+
logError: message => {
|
|
49
|
+
errorLines.push(message)
|
|
43
50
|
}
|
|
44
51
|
})
|
|
45
52
|
if (code !== 0) {
|
|
53
|
+
const detail = errorLines.length > 0 ? `:\n${errorLines.join('\n')}` : ''
|
|
46
54
|
violations.push({
|
|
47
55
|
ruleId: ctx.ruleId,
|
|
48
56
|
concernId: ctx.concernId,
|
|
49
57
|
reason: 'markdownlint',
|
|
50
|
-
message:
|
|
58
|
+
message: `markdownlint знайшов порушення у *.md/*.mdc (text.mdc)${detail}`
|
|
51
59
|
})
|
|
52
60
|
}
|
|
53
61
|
|