Редактура русскоязычных текстов по Hearthstone. Система правит грамотность и шаблонность, не трогая авторский голос — и умеет доказать, что не тронула.
Полный контракт работы агентов находится в AGENTS.md, а
проектный набор инженерных скиллов и правила выбора — в
.agents/profile.yaml и
.agents/README.md. Набор закреплён на конкретной
ревизии центрального каталога Manacost Labs и не заменяет редакторские правила
из CLAUDE.md, СТИЛЬ.md и ГОЛОС.md.
Обычный редакторский промпт держится на вкусе: «пиши живо», «не используй канцелярит», «сохраняй стиль автора». Проверить такие указания нельзя, поэтому редактор незаметно подменяет голос автора своим.
Здесь иначе: все решения опираются на замеры корпуса из 49 опубликованных гайдов — 1,67 млн знаков, 16 662 предложения. Если правило расходится с тем, как автор пишет на самом деле, отменяется правило, а не автор.
Актуальное evidence и архив автора разделены:
- research intelligence определяет, что правильно сказать на текущем patch/meta epoch;
- corpus intelligence определяет, как это звучит у автора.
В режиме GUIDE источники остаются за кулисами: читатель получает совет, а не пересказ реплеев, HSGuru и Reddit. ANALYSIS и REPORT разрешают статистическую форму.
Так были отменены 6 правил словаря из 12. Словарь требовал менять «винрейт» на «процент побед» — в корпусе 68 против 17. «Лейтгейм» на «позднюю игру» — 199 против 8. «Добор» на «взятие карт» — 243 против нуля.
| Инструмент | Задача |
|---|---|
markers.py |
30 маркеров шаблонного текста в трёх уровнях: убрать / переписать / смотреть |
soul.py |
детектор живого — следит, чтобы правка не вычистила голос |
rhythm.py |
разброс длин предложений против авторской нормы |
cards.py |
сверка названий карт с локализацией, с морфологией |
structure.py |
состав и порядок разделов гайда, охват матч-апов |
consistency.py |
согласованность внутри текста: разнобой, спорные советы, числа |
echo.py |
перекличка с архивом: где автор писал об этом раньше |
precedent.py |
как автор пишет это слово на самом деле |
archive.py |
сводный аудит всех гайдов сразу |
report.py |
отчёт о правке: что изменилось и на сколько |
author.py |
балл 0–10: соответствие текста авторской норме |
selftest.py |
регрессия правил по корпусу |
guide_voice.py |
не пропускает research-report tone в GUIDE |
clarity.py |
проверка понятности статьи: роли терминов, тезис и нагрузка текста |
certainty_guard.py |
не даёт LOW/MEDIUM claim стать категоричным |
semantic_diff.py |
ловит смену отрицания, чисел и Guide Claim Contract |
rewrite_gate.py |
затвор переплавки: результат против нормы автора, а не против исходника |
claims.py |
утверждения исходника и их покрытие: карты, советы, отрицания, классы |
elegance.py |
аккуратность: номинализации, серии начал, конкретика |
evalscore.py |
оценка переплавки по кейсам tests/evals/ |
Правка бережёт авторский текст. Но если на входе ИИ-слоп или канцелярит, беречь нечего: исходник становится источником фактов, а форма строится заново — по скелету жанра из профиля и по манере автора из корпуса.
editor-team claims исходник.md # что обязано выжить
editor-team outline validate план.json --source исходник.md # план: полнота и невыдумывание
editor-team validate-edit исходник.md результат.md --depth переплавка --declared-missing matchups
python3 tools/run_evals.py --inputs-only # 16 кейсов: слоп должен проваливаться
# с настоящей моделью — ночной воркфлоу «Ночные эвалы переплавки» (нужен секрет EDITOR_API_KEY)В шлюзе — "mode": "переплавка" или первое слово сообщения. Два прохода: план (JSON, проверяется сайдкаром) и проза по плану; затвор сравнивает результат с нормой автора и с утверждениями исходника. Раздел без материала не выдумывается, а честно объявляется отсутствующим. Пороги стоят за краем корпуса: затвор не отвергает опубликованные гайды, это проверяет selftest.py.
Evidence-hidden затвор
editor-team audit guide.md --mode GUIDE
editor-team audit analysis.md --mode ANALYSIS
editor-team validate-edit before.md after.md \
--claims-before claims.json --claims-after claims-after.json \
--current-patch 36.4 --current-meta-epoch 2026-08-31FACTUAL_SEMANTIC_DRIFT, CERTAINTY_DRIFT и STALE_EVIDENCE отклоняют правку. Исходные source/replay данные не попадают в промпт редактора: только claim_id, meaning, confidence, patch и meta_epoch.
# снача candidate: baseline не меняется
editor-team corpus add guide.md --published-at 2026-08-30 --patch 36.4
# только явное решение человека активирует текст
editor-team corpus approve GUIDE_ID
editor-team corpus reject GUIDE_ID
editor-team corpus inspect
editor-team corpus versions
editor-team corpus compare v1 v2
editor-team corpus rollback v1
# отдельный исторический архив Полей сражений из TXT
editor-team corpus import-bg "/path/to/гайды по полям"
editor-team corpus inspect --collection bg
# обычные гайды: полный учёт файлов и дедупликация против PDF-корпуса
editor-team corpus import-guides "/path/to/old-koloda-articles-guides"
editor-team corpus inspect --collection archiveКаждая активация считает global и genre baseline, robust statistics, показывает before/after и drift, а затем запускает regression на candidate state. Manifest активируется атомарно только после PASS. TXT-архивы импортируются пакетно, не меняют исходники и по умолчанию остаются candidate + historical/style_only, без права подтверждать текущую мету и менять baseline до ручного approval.
В архиве old-koloda-articles-guides учтены все 348 файлов: 297 уникальных гайдов добавлены как candidates, 49 PDF/TXT-дублей отсеяны, 2 служебных файла пропущены, ошибок нет. Подробнее — docs/corpus-learning.md.
Обычный редактор видит только тот текст, который перед ним. Здесь при правке поднимается архив: echo.py находит места, где автор писал о том же, и задаёт три вопроса — не противоречит ли новое старому, одинаково ли названы одинаковые вещи, не переписывается ли заново то, что уже сформулировано.
Поиск идёт по редким словам — названиям карт и архетипов, а не по «колоде» и «мете», — и требует минимум двух общих редких слов: одно бывает случайностью. Индекс на 3930 абзацев строится за десятые доли секунды, без внешних зависимостей.
archive.py прогоняет все проверки по всем гайдам сразу. На корпусе он нашёл в уже опубликованном:
── АПОСТРОФ в названии (25 в 3 видах) — чинить в первую очередь, проверка точная
«Зул’джин» → «Зул'джин» ×12
«КелТузад» → «Кел'Тузад» ×7
«Аралон» → «Ара'лон» ×6
── ТИРЕ в названии (15 в 2 видах)
«Алдор - искатель истины» → «Алдор – искатель истины» ×14
Плюс 119 расхождений в регистре, 14 гайдов с ровным ритмом и 2 с дырами в структуре. Находки, вызванные переносами строк при извлечении из PDF, помечаются отдельно и в итог не идут.
Обычные редакторские метрики измеряют только плохое, и это ловушка: текст можно вычистить до стерильности, и все показатели улучшатся.
soul.py считает противоположное — то, чем автор разговаривает с читателем. Нормы сняты с корпуса, на 1000 слов:
| Сигнал | Норма | Пример |
|---|---|---|
| обращение к читателю | 11,2 | «вы найдёте», «вам будет некогда ждать» |
| императив читателю | 5,1 | «оставляйте», «раскапывайте», «не бойтесь» |
| уступка и поворот | 4,2 | «Но» в начале, «хотя», «зато» |
| короткое предложение | 7,6 | фраза до 8 слов рядом с длинным периодом |
| скобка с пояснением | 1,3 | живое отступление |
Правило: снижение сигнала требует проверить конкретное изменённое место, но само по себе не делает правку плохой. Жёстко отклоняется только системное выравнивание голоса: когда статья в целом падает ниже нижней нормы. Локально убрать обращение, скобку или короткую фразу можно, если это устраняет доказанную проблему и новый вариант сильнее исходника по смыслу, ясности и пользе для читателя.
Три из пяти сигналов — ровно то, что аккуратный редактор вычищает первым. «Но» в начале склеивает с предыдущим предложением, императив обезличивает в «стоит оставить», уступку с «хотя» вырезает как лишнее хеджирование.
На проверке: живой абзац, «причёсанный» как это сделал бы старательный редактор, потерял обращения 35,7 → 0, императивы 107 → 0, уступки 71 → 0. Ни одного маркера шаблона при этом не появилось — без детектора живого такая правка прошла бы как безупречная.
У исходника преимущество. Для каждого спорного места редактор сначала называет проблему читателя, затем идёт по лестнице: оставить → исправить локально → переписать. Более сильное вмешательство допустимо, только если меньшее не решает проблему. Кандидат принимается не потому, что стал короче или набрал лучшие метрики, а только если он не хуже исходника по смыслу, логике, конкретике, голосу, жанру и практической пользе — и заметно лучше хотя бы по одному из этих пунктов.
Правило «названия не трогать» защищает от догадок, но не от опечаток. Здесь третий путь: справочник на 6602 карты плюс морфологический разбор через pymorphy3.
Ошибкой не считаются падежные формы («Балинды», «Бранном»), короткие имена («Балинда» вместо «Балинда Каменный Очаг», «Монетка» вместо «Фальшивая монетка») и слова классов и архетипов.
Проверяются апостроф, тире, регистр в многословных названиях — точно, без догадок — и опечатки нечётким сравнением с обязательным совпадением корня.
Найденное в опубликованных текстах: КелТузад без апострофа 7 раз, Зул’джин с кривым апострофом 13 раз, Кельтас вместо Кель'тас.
Длинный гайд легко начинает спорить сам с собой. consistency.py ловит три вещи: разнобой в написании («Пират Воин» и «Пират воин», «матч-ап» и «матчап»), карту, которую в одном месте советуют оставлять, а в другом сбрасывать, и заявленное число карт против фактически перечисленных.
Первая проверка надёжная, вторая — подсказка без вердикта: разбирать смысл советов автоматически нельзя, там легко наврать.
Точность далась не сразу. Первая версия дала 24 ложные находки на одном гайде: заглавная после точки считалась разнобоем, названия разделов в заголовках и кавычках — тоже, а однословная карта «К оружию!» ловила любое упоминание оружия. После разбора этих причин на вычитанном корпусе осталось 6 находок на 49 гайдов, и все шесть настоящие.
common.py держит пути, бутстрап .venv, морфологию с кэшем и разбиение текста. Пути раньше считал каждый скрипт сам — и дважды ошибся в числе уровней вверх. В scripts/README.md записано, как добавить новую проверку и на какие грабли уже наступали.
Система принципиально не даёт оценку «текст стал лучше на N%»: такой величины нет. Но одно число всё же считается честно — соответствие авторской норме, из шести измеримых составляющих с открытой формулой.
5.4 / 10 — соответствие авторской норме
живое 1.9 ██········ 9.6 сигн./1000 сл. при норме 29.4
ритм 4.2 ████······ разброс/среднее 0.33 при норме 0.51
чистота 10.0 ██████████ 0 маркеров = 0.0/1000 сл.
названия 10.0 ██████████ 0 расхождений с локализацией
согласованность 10.0 ██████████ 0 мест с разнобоем
структура 3.0 ███······· 1 из 5 разделов, матч-апы 6/11
Шкала откалибрована по корпусу: медиана опубликованных гайдов 9,1, квартили 8,4 / 9,1 / 9,4, минимум 6,9. Десятка не эталон — до неё не дотягивает ни один опубликованный текст.
Под баллом — блок «Что подтянуть». Каждая рекомендация выводится из замера и указывает адрес в тексте:
живое ни одного совета глаголом. Безличных оборотов 10,
первый — стр. 3: «здесь приходится думать над…»
└ в твоих гайдах в таких местах стоит «оставляйте», «держите»
ритм самое длинное — 30 слов: «Друид быстро разгоняется…»
└ разрубить его надвое дешевле, чем удлинять остальные
структура нет разделов: Сборки, Декбилдинг, Муллиган, Стратегия
└ в корпусе они есть в 96–100% гайдов
Советов без адреса система не даёт: «пиши живее» и «добавь динамики» запрещены наравне с выдуманным процентом качества. Рекомендация либо опирается на замер и указывает место, либо не выводится. Формулировки при этом остаются авторскими — панель показывает, где смотреть, но не вписывает за автора ни обращений, ни императивов.
Что балл не значит: высокий — не «шедевр», а «написано в твоей манере»; низкий — не «плохо», а «не похоже на корпус», и это может быть осознанным экспериментом.
Корпус — это ещё и тест-набор заведомо хорошего письма. Если правила начинают ругаться на опубликованные тексты, значит они ловят авторскую манеру, а не шаблон.
маркеров всего 303 = 12.2 на 10к слов (порог 20.0)
живых сигналов 29.8 на 1000 слов (минимум 20.0)
структура опознана 47 из 49 гайдов (96%) (минимум 90%)
ОК — правила не задевают опубликованные тексты
Так найдены и сняты четыре дефекта, включая правило про парные тире: 3915 ложных срабатываний, 88% всего шума, и оно прямо противоречило листу стиля, где длинное тире записано как основной знак. Общая частота ложных срабатываний упала со 179,2 до 12,2 на 10 000 слов — в пятнадцать раз.
CLAUDE.md договор редактуры: режимы, закрытый список причин, словарь
ГОЛОС.md слепок авторской манеры по корпусу
СТИЛЬ.md решённые вопросы оформления и порядок старшинства правил
гайды/ корпус: 49 опубликованных гайдов
.claude/skills/
hs-edit/ редактура
references/ маркеры, ритм и голос, специфика Hearthstone
assets/ каталог маркеров, справочник карт
scripts/ инструменты
hs-research/ поиск актуального: Reddit, YouTube, мета-сайты
sidecars/nlp/ Natasha + Razdel: offsets, леммы, сущности и повторы
go/internal/hunspell/ подсказки Hunspell с allowlist игровых терминов
go/internal/markdownlint/ безопасный markdownlint CLI-adapter
evals/ Promptfoo baseline/candidate и 48 обезличенных кейсов
Правка допустима, только если попадает в один из восьми пунктов: ошибка, согласование, двусмысленность, слышимый повтор, тяжёлая конструкция, словарь, абзац-полотно, сломанная логическая связка. Всё остальное остаётся авторским — в том числе то, что редактор написал бы иначе.
Поверх списка работает фильтр четырёх вопросов: удаление, упрощение, речь, информация. Список говорит, когда правка допустима; фильтр — стоит ли она того.
- Названия карт и защищённые слова
СТИЛЬ.md— оформлениеГОЛОС.md— мера и ритмCLAUDE.md— словарь и закрытый список- Общая грамотность
git clone https://github.com/Manacost-Labs/EditorTeam.git
cd EditorTeam
python3 -m venv .venv && .venv/bin/pip install pymorphy3 pymorphy3-dicts-ruГотовый ChatGPT Work/Codex plugin лежит в release/editor-team-chatgpt-work-plugin-1.6.0.zip. Повторить сборку с автономной морфологией, corpus и smoke test:
.venv/bin/python tools/build_skill.py --release --пробаКонтрольная сумма записана в release/SHA256SUMS.
Чем релиз отстал от исходников, показывает python3 tools/build_skill.py --дрейф. Правило: пока в release/ лежит версия, равная PLUGIN_VERSION в сборщике, содержимое обязано совпадать с исходниками; изменили исходники после релиза — поднимите PLUGIN_VERSION, тест это проверяет.
Морфология нужна только для cards.py — остальные инструменты работают на голом Python 3.
Основной backend запускается из go/:
EDITOR_PROVIDER=none go run ./cmd/editorteamОн сохраняет /health, /analyze, /validate, /rules и /outline/validate,
а новый staged pipeline доступен по POST /v2/edit с режимами proofread,
edit и rewrite. Проверки LanguageTool и Vale подключаются через
LANGUAGETOOL_URL, VALE_BIN и VALE_CONFIG; старые Python-анализаторы
остаются adapter-ом до переноса морфологии. Подробности и пример JSON — в
MIGRATION.md и GO_MIGRATION_PLAN.md.
Полный набор внешних проверок поднимается через docker compose up --build:
LanguageTool и Natasha/Razdel запускаются отдельными сервисами, а Hunspell,
Vale и markdownlint вызываются из Go с таймаутом и ограничением вывода. Если
инструмент недоступен, ответ содержит analyzer_unavailable и
checks_complete=false — текст не выдаётся за полностью проверенный.
Образ сам доставляет Vale 3.17.0 и русский Hunspell-словарь
ru-spelling-dictionary 1.0.8 с проверкой SHA-256 для amd64 и
arm64. Игровой allowlist маскирует термины, но Hunspell никогда
не исправляет текст автоматически.
Evaluation запускается командой npx promptfoo eval -c evals/promptfooconfig.yaml;
описание кейсов, baseline/candidate и дополнительных judge-метрик находится в
docs/evals.md. Правила Vale, их профили и тесты описаны в
docs/sidecars.md, команды диагностики стека — в
docs/editor-toolchain.md.
Production gateway работает как AG-UI сотрудник TeamBot. Для ссылки Google Docs модель получает только read-only инструменты, а принятая анализатором правка регистрируется отдельным внутренним запросом:
EDITOR_OPENBOT_URL=http://openbot:3001
EDITOR_OPENBOT_TOKEN=<service token TeamBot>
Gateway передаёт TeamBot подписанный openbotRun, document ID, проверенный source и candidate.
Сервисный токен не входит в model prompt или forwardedProps. TeamBot строит точные диапазоны,
хранит предложение зашифрованно и возвращает только ссылку на owner-only экран diff. Документ
меняется после кнопки пользователя одной revision-fenced операцией; write-tool модели не выдаётся.
Если автоматическое сопоставление небезопасно (несколько tabs, таблица, структурная правка, форматированный абзац или усечённое чтение), исправленный текст остаётся в чате без ссылки записи.
Справочник карт обновляется отдельно:
python3 .claude/skills/hs-edit/scripts/cards.py --обновитьОткрыть папку в Claude Code и прислать текст. CLAUDE.md подхватывается сам.
Режим задаётся первым словом: лёгкая — только ошибки, без слова — обычная правка, глубокая — со структурой, переплавка — пересборка плохого текста по скелету жанра и манере автора, дифф — сначала список правок с причинами, рисёрч — сбор свежих данных по игре.
Проверить правку целиком:
python3 .claude/skills/hs-edit/scripts/report.py до.md после.md
python3 .claude/skills/hs-edit/scripts/soul.py после.md --было до.mdОценки «текст стал лучше на N%» система не даёт: такой величины не существует, и любое число было бы выдуманным. Вместо неё — затронутый процент текста, изменение длины, маркеры до и после, ритм и живые сигналы.
Каталог маркеров собран из better-writing, humanizer, author-toolkit и claude-skills-journalism и переписан под русский язык: английские правила про em dash, passive voice и списки слов вроде «delve» в русском не работают, поэтому взята механика, а не словари.
Справочник карт — HearthstoneJSON, русская локализация.