@7n/rules 1.49.20 → 1.49.22

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.49.22] - 2026-07-27
4
+
5
+ ### Fixed
6
+
7
+ - Retry initial PR check discovery and prune rebuildable forensic dependencies
8
+
9
+ ## [1.49.21] - 2026-07-27
10
+
11
+ ### Fixed
12
+
13
+ - v8r: validate VS Code extension recommendations with the local catalog
14
+
3
15
  ## [1.49.20] - 2026-07-27
4
16
 
5
17
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@7n/rules",
3
- "version": "1.49.20",
3
+ "version": "1.49.22",
4
4
  "description": "CLI еталонних правил і skills (префікс n-): синк у репозиторій, дельта-lint, конформність",
5
5
  "keywords": [
6
6
  "cli",
@@ -15,13 +15,15 @@
15
15
  },
16
16
  {
17
17
  "name": "any.json",
18
- "description": "Заглушка-схема (приймає будь-який JSON) — bespoke tool-конфіги й one-off артефакти без офіційної схеми",
18
+ "description": "Заглушка-схема (приймає будь-який JSON) — bespoke tool-конфіги, VS Code extension recommendations і one-off артефакти без офіційної схеми",
19
19
  "url": "any.json",
20
20
  "fileMatch": [
21
21
  ".licensee.json",
22
22
  "**/.licensee.json",
23
23
  ".kubescape-exceptions.json",
24
24
  "**/.kubescape-exceptions.json",
25
+ ".vscode/extensions.json",
26
+ "**/.vscode/extensions.json",
25
27
  "docs/specs/**/*.json",
26
28
  "docs/plans/**/*.json",
27
29
  "any.json",
@@ -3,7 +3,7 @@ type: JS Module
3
3
  title: orchestrate.mjs
4
4
  resource: npm/skills/git-reconcile/js/orchestrate.mjs
5
5
  docgen:
6
- crc: f87f22fc
6
+ crc: 59ea7f47
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
9
  ---
@@ -20,11 +20,11 @@ docgen:
20
20
 
21
21
  Для корисної групи JS створює ізольований worktree від policy base, застосовує commits або stash і відсікає порожній tree diff. `captureCachedBehaviorBaseline` кешує Promise baseline за base OID, щоб concurrent PR-групи не дублювали test run. `validateBehaviorState` перевіряє Git state, scoped docs/lint, tests відносно baseline і changelog. `remediateBehaviorState` запускає canonical fixers для code та non-code directories; після remediation final gates повторюються у read-only режимі.
22
22
 
23
- `runAsync` запускає install, tests, lint і PR checks без блокування event loop. Після push та `gh pr create` функція `verifyPullRequestReadiness` очікує terminal checks і передає набори PR/base checks у `classifyPullRequestChecks`. Успішний outcome `pr-created` повертається лише для `ready`; regression, baseline-red, pending, timeout або unreadable checks зберігають branch, URL і worktree та блокують cleanup.
23
+ `runAsync` запускає install, tests, lint і PR checks без блокування event loop. Після push та `gh pr create` функція `verifyPullRequestReadiness` очікує terminal checks і передає набори PR/base checks у `classifyPullRequestChecks`. Якщо initial rollup порожній, orchestration дає GitHub коротке вікно на реєстрацію checks і повторює watch. Успішний outcome `pr-created` повертається лише для `ready`; regression, baseline-red, pending, timeout або unreadable checks зберігають branch, URL і worktree та блокують cleanup.
24
24
 
25
25
  `commitPendingChanges` створює додатковий commit лише для staged remediation. Чистий index після перенесення branch-source є валідним, коли корисні commits уже присутні в `HEAD`. Порожній PR check rollup вважається непідтвердженим, а не успішним.
26
26
 
27
- `cleanupSource` видаляє лише точний доказово безпечний branch/stash. Cleanup review-source дозволений після `drop` або коли всі його групи завершились `pr-created` чи `patch-equivalent`; `failed` і всі `pr-checks-*` outcomes лишають source та forensic worktree.
27
+ `pruneForensicDependencies` прибирає лише rebuildable `node_modules` зі збереженого forensic worktree. `cleanupSource` видаляє лише точний доказово безпечний branch/stash. Cleanup review-source дозволений після `drop` або коли всі його групи завершились `pr-created` чи `patch-equivalent`; `failed` і всі `pr-checks-*` outcomes лишають source та forensic worktree.
28
28
 
29
29
  ## Публічний API
30
30
 
@@ -36,6 +36,7 @@ docgen:
36
36
  - `captureBehaviorBaseline`, `captureCachedBehaviorBaseline` — фіксують і кешують test baseline.
37
37
  - `validateBehaviorState`, `validateFinalProjectGates`, `remediateBehaviorState` — виконують behavioral та canonical gates.
38
38
  - `commitPendingChanges` — комітить лише staged remediation, не вимагаючи порожнього commit для вже перенесених commits.
39
+ - `pruneForensicDependencies` — звільняє rebuildable dependencies без втрати Git evidence.
39
40
  - `classifyPullRequestChecks`, `verifyPullRequestReadiness` — класифікують CI відносно base commit.
40
41
  - `formatOutcomeCounts`, `formatReport` — формують точний deterministic summary.
41
42
  - `cleanupSource`, `runGitReconcileOrchestrator` — виконують безпечний cleanup і координують повний flow.
@@ -47,6 +48,7 @@ docgen:
47
48
  - Canonical fixers охоплюють code/non-code scope, після чого gates повторюються без fix.
48
49
  - PR вважається створеним успішно лише після terminal green checks.
49
50
  - Порожній PR check rollup fail-closed зберігає forensic worktree.
51
+ - Forensic worktree зберігає Git evidence без накопичення `node_modules`.
50
52
  - CI regression, baseline-red або непідтверджений стан завершують orchestration non-zero і зберігають forensic refs/worktree.
51
53
  - Cleanup не видаляє protected, open-PR, kept, failed або `pr-checks-*` sources.
52
54
  - `spawnSync.error`, зокрема `ENOENT`, зберігається в command diagnostics.
@@ -1,10 +1,11 @@
1
1
  /** @see ./docs/orchestrate.md */
2
2
  import { spawn, spawnSync } from 'node:child_process'
3
3
  import { once } from 'node:events'
4
- import { appendFileSync, existsSync, readFileSync } from 'node:fs'
4
+ import { appendFileSync, existsSync, readFileSync, rmSync } from 'node:fs'
5
5
  import { dirname, isAbsolute, join } from 'node:path'
6
6
  import { performance } from 'node:perf_hooks'
7
7
  import { env } from 'node:process'
8
+ import { setTimeout as delay } from 'node:timers/promises'
8
9
 
9
10
  import { renderProgressLine } from '../../../scripts/lib/lint-surface/progress.mjs'
10
11
  import { readGitPolicy } from '../../../scripts/lib/git-policy.mjs'
@@ -1456,19 +1457,36 @@ export function classifyPullRequestChecks(prChecks, baseChecks) {
1456
1457
  }
1457
1458
  }
1458
1459
 
1460
+ /**
1461
+ * Видаляє лише rebuildable dependencies зі збереженого forensic worktree.
1462
+ * Git metadata, commits і tracked/untracked зміни не зачіпаються.
1463
+ * @param {string} worktreeCwd шлях worktree
1464
+ * @returns {boolean} чи було що прибрати
1465
+ */
1466
+ export function pruneForensicDependencies(worktreeCwd) {
1467
+ const dependencies = join(worktreeCwd, 'node_modules')
1468
+ if (!existsSync(dependencies)) return false
1469
+ try {
1470
+ rmSync(dependencies, { recursive: true, force: true })
1471
+ return true
1472
+ } catch {
1473
+ return false
1474
+ }
1475
+ }
1476
+
1459
1477
  /**
1460
1478
  * Чекає terminal CI state й порівнює failed checks з base commit.
1461
1479
  * @param {object} args PR context
1462
1480
  * @returns {Promise<{status:string,error?:string}>} readiness
1463
1481
  */
1464
1482
  export async function verifyPullRequestReadiness(args) {
1465
- const { url, cwd, spawnFn = spawnSync, asyncSpawnFn } = args
1483
+ const { url, cwd, spawnFn = spawnSync, asyncSpawnFn, delayFn = delay } = args
1466
1484
  const longRunner = resolveAsyncSpawn(spawnFn, asyncSpawnFn)
1467
1485
  await runAsync('gh', ['pr', 'checks', url, '--watch', '--interval', '10'], cwd, longRunner, {
1468
1486
  allowFailure: true,
1469
1487
  timeoutMs: PR_CHECK_TIMEOUT_MS
1470
1488
  })
1471
- const view = await runAsync(
1489
+ let view = await runAsync(
1472
1490
  'gh',
1473
1491
  ['pr', 'view', url, '--json', 'statusCheckRollup,baseRefOid'],
1474
1492
  cwd,
@@ -1478,7 +1496,28 @@ export async function verifyPullRequestReadiness(args) {
1478
1496
  if (view.status !== 0) {
1479
1497
  return { status: 'pr-checks-unverified', error: `Не вдалося прочитати PR checks: ${view.stderr || view.error}` }
1480
1498
  }
1481
- const pr = parseJson(view.stdout, null)
1499
+ let pr = parseJson(view.stdout, null)
1500
+ if (pr && Array.isArray(pr.statusCheckRollup) && pr.statusCheckRollup.length === 0) {
1501
+ await delayFn(10_000)
1502
+ await runAsync('gh', ['pr', 'checks', url, '--watch', '--interval', '10'], cwd, longRunner, {
1503
+ allowFailure: true,
1504
+ timeoutMs: PR_CHECK_TIMEOUT_MS
1505
+ })
1506
+ view = await runAsync(
1507
+ 'gh',
1508
+ ['pr', 'view', url, '--json', 'statusCheckRollup,baseRefOid'],
1509
+ cwd,
1510
+ longRunner,
1511
+ { allowFailure: true }
1512
+ )
1513
+ if (view.status !== 0) {
1514
+ return {
1515
+ status: 'pr-checks-unverified',
1516
+ error: `Не вдалося повторно прочитати PR checks: ${view.stderr || view.error}`
1517
+ }
1518
+ }
1519
+ pr = parseJson(view.stdout, null)
1520
+ }
1482
1521
  if (!pr || !Array.isArray(pr.statusCheckRollup) || !pr.baseRefOid) {
1483
1522
  return { status: 'pr-checks-unverified', error: 'GitHub повернув неповний PR check rollup' }
1484
1523
  }
@@ -1669,6 +1708,8 @@ async function createPullRequest(args) {
1669
1708
  asyncSpawnFn: deps.asyncSpawnFn
1670
1709
  })
1671
1710
  if (readiness.status !== 'ready') {
1711
+ onProgress('prune forensic dependencies')
1712
+ pruneForensicDependencies(worktree.cwd)
1672
1713
  return {
1673
1714
  status: readiness.status,
1674
1715
  error: readiness.error,
@@ -1682,6 +1723,10 @@ async function createPullRequest(args) {
1682
1723
  return { status: 'pr-created', url: createdPr, branch: worktree.branch }
1683
1724
  } catch (error) {
1684
1725
  const setup = /** @type {{branch?:string,worktree?:string}} */ (error)
1726
+ if (worktree?.cwd) {
1727
+ onProgress('prune forensic dependencies')
1728
+ pruneForensicDependencies(worktree.cwd)
1729
+ }
1685
1730
  return {
1686
1731
  status: 'failed',
1687
1732
  error: error instanceof Error ? error.message : String(error),