Статический анализ · CI · AI-сводки · OpenClaw · 2026

2026: фронтенд на удалённом Mac —
слияние ESLint JSON и Stylelint в одну сводку ворот и читаемый комментарий в PR через шлюз OpenClaw

27.04.2026 Фронтенд-команды · CI и LLM-дайджесты 9 мин чтения

Для кого: фронтенд с CI и AI-сводкой, где нужны OpenClaw, ESLint JSON, Stylelint, удалённый Mac, сводка PR. Ниже — HowTo под Node 22+: контракт путей, агрегация на шлюзе, один Markdown. См. Playwright sharding, knip JSON, токены E2E.

00 Три типичных ограничения

  1. Два независимых потока вывода ESLint и Stylelint превращают сводку PR в длинный шум, который ревьюеры игнорируют.
  2. Абсолютные пути в ESLint JSON и поле source у Stylelint расходятся между ноутбуком и раннером, из-за чего шлюз OpenClaw дублирует или теряет строки.
  3. В комментарии часто копируют приватные preview URL; без доступа к организации ссылки мёртвые, а токены нельзя светить в Markdown.

01 Матрица ответственности

Раннер пишет ESLint JSON и Stylelint в файлы; шлюз OpenClaw на удалённом Mac нормализует пути, merge и пороги; Git-провайдер получает один upsert комментария. Так вы избегаете двойного счёта и ложного зелёного статуса.

Слой Фокус Риск
CI / локалка JSON в .openclaw/reports Дрейф stdout и версий
Шлюз lint_gate.json и пороги Ложный pass без merge
PR API Идемпотентный комментарий 429 и дубликаты

02 HowTo: воспроизводимый чек-лист

Одинаковые шаги на локалке и на удалённом Mac убирают расхождение путей.

  1. Node 22+ в engines и матрице CI.
  2. eslint . -f json -o .openclaw/reports/eslint.json и stylelint "**/*.{css,scss}" --formatter json -o .openclaw/reports/stylelint.json.
  3. На шлюз: файлы плюс GIT_SHA, PR_NUMBER, опционально BASE_REF.
  4. Merge пишет pr_lint_gate_summary.md, код выхода по порогам, затем REST комментария.
  5. LLM читает только merge JSON или укороченный Markdown.

03 Единый формат JSON локально и в CI

Расхождение ноутбука и CI ломает сводку PR. Обрежьте абсолютные filePath и source до относительных на шлюзе; всегда -f json, --formatter json и -o, stdout пустой.

package.json (Node 22+):

{
  "engines": { "node": ">=22" },
  "scripts": {
    "lint:eslint:json": "mkdir -p .openclaw/reports && eslint . -f json -o .openclaw/reports/eslint.json",
    "lint:stylelint:json": "mkdir -p .openclaw/reports && stylelint \"**/*.{css,scss}\" --formatter json -o .openclaw/reports/stylelint.json",
    "lint:json:all": "npm run lint:eslint:json && npm run lint:stylelint:json"
  }
}

04 Агрегация на шлюзе OpenClaw и пороги

Шлюз строит lint_gate/v1 из готовых отчётов: счётчики, топ правил, файлы. Severity 2 ESLint — ошибка; бюджет предупреждений Stylelint, например max_stylelint_warnings: 12 в YAML на релизе.

merge (Node 22 ESM):

// tools/merge-lint-gate.mjs — на шлюзе OpenClaw после доставки отчётов
import { readFile, writeFile } from "node:fs/promises";

const root = process.env.REPO_ROOT ?? "";
const strip = (p) => (p && p.startsWith(root) ? p.slice(root.length + 1) : p);

const eslint = JSON.parse(await readFile(".openclaw/reports/eslint.json", "utf8"));
const style = JSON.parse(await readFile(".openclaw/reports/stylelint.json", "utf8"));

const rows = [];
for (const f of Array.isArray(eslint) ? eslint : []) {
  for (const m of f.messages ?? []) {
    rows.push({ tool: "eslint", file: strip(f.filePath), rule: m.ruleId ?? "syntax", sev: m.severity });
  }
}
for (const f of Array.isArray(style) ? style : []) {
  for (const w of f.warnings ?? []) {
    rows.push({ tool: "stylelint", file: strip(f.source), rule: w.rule ?? "unknown", sev: w.severity });
  }
}

const gate = {
  version: 1,
  errors: rows.filter((r) => r.sev === 2 || r.sev === "error").length,
  warnings: rows.filter((r) => r.sev === 1 || r.sev === "warning").length,
  topRules: [],
};
await writeFile(".openclaw/reports/lint_gate.json", JSON.stringify(gate, null, 2));

Заполните topRules и версионируйте схему для OpenClaw.

05 Повтор при сбоях и усечение сводки

Транспорт и 429 — backoff с джиттером с IP удалённого Mac; линтер без повтора, кроме битого JSON после краша.

Усечьте Markdown сводки PR (~120 строк), укажите «ещё N в артефактах CI», без base64 в комментарии.

bash:

#!/usr/bin/env bash
set -euo pipefail
attempt=0 max=4
until node tools/post-pr-lint-summary.mjs; do
  code=$?
  attempt=$((attempt + 1))
  if [[ "$code" -eq 77 ]] && [[ "$attempt" -lt "$max" ]]; then
    sleep $((2 ** attempt + RANDOM % 3))
    continue
  fi
  exit "$code"
done

Код 77 в скрипте — только повторяемые ошибки API.

06 API комментариев: идея без приватных URL

REST к issue PR или review-comment; токен только на шлюзе, не в Markdown.

Маркер <!-- openclaw-lint-gate:HASH --> по репо, PR и хешу JSON — ищите старый комментарий бота и обновляйте.

Не вставляйте приватные preview URL с обязательным входом; укажите номер джоба или публичную справку. Тело держите сильно ниже ~65k символов GitHub: режьте списки, не счётчики.

  • Node 22+ — целевой major для плоского ESLint и нативных плагинов.
  • Ориентир 120 строк Markdown сводки до усечения списка файлов.
  • До четырёх повторов POST при коде 77 в примере bash.
Итог

Node 22+, каталог .openclaw/reports и один lint_gate.json связывают локалку и удалённый Mac. Когда ESLint JSON и Stylelint merge на OpenClaw, сводка PR даёт счётчики, топ правил, усечённый список и pass или fail для CI плюс AI.

Навигация по сайту: главная, помощь, блог.

Удалённый Mac · Node 22 · OpenClaw

Арендуйте узел под шлюз и линт-ворота

Нужен тот же Apple Silicon, что и у смоука Safari? Оформите удалённый Mac на тарифах, откройте покупку без обязательной регистрации и закрепите OpenClaw рядом с ESLint JSON и Stylelint в одной цепочке поставки.

Единая сводка ворот Комментарии в PR Apple Silicon
Mac под ESLint и Stylelint