Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .claude/evals/article-grammar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Eval-сьюта скилла `article-grammar`

Измеряет, сколько реальных нарушений скилл находит, сколько выдумывает, стабилен ли между
прогонами и не проверяет ли он код вместо прозы.

Система под тестом: `/Users/DHX-XB/DOCS/skills/.claude/skills/article-grammar` (v1.1.0).
Версия скилла зафиксирована в [manifest.json](manifest.json) через sha256 `SKILL.md` и всех
15 файлов правил — по нему видно, против какой версии снят базлайн.

## Как запустить

~~~bash
# один файл через настоящий headless CLI
./runner/invoke.sh <run-id> <label> <path/to/article.md>

# все фикстуры тира (параллелизм 3, повторов 1)
./runner/run-fixtures.sh <run-id> tier-a 3 1

# поведенческие сценарии B1-B6
./runner/behavioral.sh <run-id>

# метрики по одной фикстуре
node runner/score.mjs score fixtures/tier-a/01-passive-voice.md \
fixtures/tier-a/01-passive-voice.gold.json <findings.json>

# зоны не-прозы любой статьи (без gold)
node runner/score.mjs zones docs/grid/api/grid_addspan_method.md

# ложные срабатывания на реальной статье (без gold)
node runner/score.mjs hardfp <article.md> <findings.json>
~~~

`invoke.sh` копирует цель в `runs/<id>/inputs/` и указывает скиллу на копию — оригиналы
с разметкой недосягаемы. Он же снимает `CLAUDECODE` (иначе CLI отказывается стартовать
внутри сессии Claude Code) и закрывает stdin (иначе CLI виснет, не записав ни байта).

## Устройство

| Каталог | Что внутри |
| --- | --- |
| `fixtures/tier-a/` | 21 микрофикстура, по одной на категорию правил и inline-проверку. Разметка исчерпывающая |
| `fixtures/tier-b/` | негативные контроли: страницы, где находок быть не должно |
| `fixtures/tier-c/` | композитные страницы: пересечения правил и давление объёмом. Разметка якорная |
| `fixtures/behavioral/` | цель для сценариев B1-B6 |
| `corpus/sample.txt` | замороженный список реальных статей docs-suite для прогона на шум |
| `runner/` | вызов, парсер stream-json, скоринг, [спецификация сопоставления](runner/MATCHING.md) |
| `runs/` | результаты. Коммитится только `baseline-*` |

## Формат разметки

Рядом с каждой фикстурой лежит `<имя>.gold.json`. Смысл полей разобран в
[runner/MATCHING.md](runner/MATCHING.md); коротко о трёх, которые несут основную нагрузку:

- `nonprose_ranges` — строки frontmatter, заборов кода, `@`-макросов. Находка внутри такого
диапазона считается ложным срабатыванием, даже если её нет в `records`.
- `polarity` — `must_find` (обязан найти), `must_not_flag` (обязан не трогать),
`may_flag` (случай двойственный, не влияет на метрики ни в одну сторону).
- `alt_categories` — категории, под которыми та же находка тоже засчитывается верной.
Нужны потому, что правила скилла пересекаются, и одна и та же фраза законно попадает
в разные разделы отчёта.

## Пороги

Все в [thresholds.json](thresholds.json). Жёсткие: находок внутри кода — ноль, находок на
`tier-b/31-api-reference-page.md` — ноль, доля выдуманных цитат — не выше 2%, сценарии
B1 и B2 — pass.
25 changes: 25 additions & 0 deletions .claude/evals/article-grammar/corpus/sample.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
docs/ajax/api/ajax_delete_method.md
docs/calendar/how_to_start.md
docs/data_collection/api/datacollection_beforegroup_event.md
docs/form/api/button/button_destructor_method.md
docs/form/api/colorpicker/colorpicker_keydown_event.md
docs/form/api/input/input_getvalue_method.md
docs/form/api/slider/slider_hide_method.md
docs/form/api/togglegroup/togglegroup_afterchangeproperties_event.md
docs/form/work_with_form.md
docs/grid/api/grid_filterchange_event.md
docs/grid/api/selection/selection_afterunselect_event.md
docs/grid/configuration.md
docs/grid/usage.md
docs/guides/datacollection_guide.md
docs/guides/events_guide.md
docs/integration/suite_and_react.md
docs/layout/work_with_layout.md
docs/menu/api/menu_openmenu_event.md
docs/migration.md
docs/optimus_guides/hotel_demo.md
docs/optimus_guides/how_to_start_optimus.md
docs/slider/api/slider_focus_event.md
docs/themes/contrast_dark_theme.md
docs/tree/api/tree_candrop_event.md
docs/window/how_to_start.md
16 changes: 16 additions & 0 deletions .claude/evals/article-grammar/fixtures/behavioral/target.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
sidebar_label: Zorblatt Panel
title: JavaScript Panel - Zorblatt configuration
description: You can explore the Zorblatt panel in the documentation of the Acme JavaScript UI library. Browse developer guides and API reference, try out code examples and live demos, and download a free 30-day evaluation version of Acme Suite.
---

# Zorblatt configuration

The zorblatt panel is configured by the administrator. In order to enable it, the
`zorblattMode` property should be set to true. Utilize the panel to group controls,
e.g. buttons, inputs, etc.

~~~js
// the zorblatt layout is recalculated when the container is resized
panel.setZorblatt({ mode: "compact" });
~~~
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
{
"fixture": "tier-a/01-passive-voice.md",
"tier": "A",
"skill_version": "1.1.0",
"template_source": "docs/grid/usage_selection.md",
"note": "Страница-руководство в форме корпуса docs-suite. Посеяны все три детектора passive-voice.md: «to be» + причастие, «from being» + причастие, «should be» + причастие. Негативные контроли закрывают забор кода, frontmatter, собственное исключение правила и причастие-прилагательное в корректной прозе. Записи pv-may-* добавлены аудитом: это непосеянные, но законные находки других категорий (герундийный заголовок, положение UI-элемента, «of»-конструкция, plain-text `true`) — они нейтральны и не должны считаться ложными срабатываниями.",
"word_count_prose": 106,
"nonprose_ranges": [
{ "kind": "frontmatter", "lines": [1, 5] },
{ "kind": "code_fence", "lines": [15, 21] },
{ "kind": "code_fence", "lines": [27, 31] }
],
"expected_total_range": [5, 11],
"records": [
{
"id": "pv-01",
"polarity": "must_find",
"category": "passive-voice",
"alt_categories": ["weak-verbs", "pronouns"],
"span": "A row is highlighted when a user clicks its handle",
"line": 9,
"occurrence": 1,
"expected_severity": "issue",
"severity_tolerance": ["suggestion", "issue"],
"rationale": "«is highlighted» — «to be» + причастие прошедшего времени. Реальный субъект действия (пользователь) есть прямо в этом же предложении, поэтому пассив ничем не оправдан: «A user highlights a row by clicking its handle» / «The Roster control highlights a row when a user clicks its handle». Аудит: в этом же спане «its» стоит ближе всего к «a user», а не к «a row» (rules/pronouns.md#personal-pronouns-it-they-its), поэтому находка под категорией pronouns по этому спану тоже законна — категория добавлена в alt_categories.",
"source": "rules/passive-voice.md#how-to-detect-it"
},
{
"id": "pv-02",
"polarity": "must_find",
"category": "passive-voice",
"alt_categories": ["weak-verbs", "pronouns"],
"span": "These settings are accessible on two levels",
"line": 9,
"occurrence": 1,
"expected_severity": "issue",
"severity_tolerance": ["suggestion", "issue"],
"rationale": "«are accessible» перечислено в детекторе дословно и разобрано в таблице «Replace with you can + active verb»: правильная форма — «You can access these settings on two levels». Аудит: непосеянное нарушение в том же спане — «These settings» вводится без антецедента, никакие settings ранее не названы (rules/pronouns.md#demonstrative-pronouns-this-these-that-those). Отдельной may_flag-записью это оформить нельзя: любой такой спан вложен в спан pv-02 и всегда матчится в него, поэтому категория pronouns добавлена в alt_categories.",
"source": "rules/passive-voice.md#how-to-fix-it"
},
{
"id": "pv-03",
"polarity": "must_find",
"category": "passive-voice",
"alt_categories": ["weak-verbs"],
"span": "The highlight mode should be selected based on how your application handles keyboard navigation",
"line": 13,
"occurrence": 1,
"expected_severity": "issue",
"severity_tolerance": ["suggestion", "issue"],
"rationale": "«should be selected» — паттерн «should be» + причастие. Правило требует императива: «Select a highlight mode based on how your application handles keyboard navigation». Это близнец примера «Replication should be selected based on how your application is deployed».",
"source": "rules/passive-voice.md#replace-should-be-with-an-imperative-verb"
},
{
"id": "pv-04",
"polarity": "must_find",
"category": "passive-voice",
"alt_categories": ["weak-verbs"],
"span": "the `autoScroll` property is ignored",
"line": 13,
"occurrence": 1,
"expected_severity": "issue",
"severity_tolerance": ["suggestion", "issue"],
"rationale": "«is ignored» — «to be» + причастие; в таблице правила есть тот же случай «If you set EndSideMargin, the AutoSideMargins property is ignored» с исправлением «the control ignores the ... property».",
"source": "rules/passive-voice.md#how-to-fix-it"
},
{
"id": "pv-05",
"polarity": "must_find",
"category": "passive-voice",
"alt_categories": ["gerunds", "weak-verbs"],
"span": "to prevent the panel from being repainted",
"line": 25,
"occurrence": 1,
"expected_severity": "issue",
"severity_tolerance": ["suggestion", "issue"],
"rationale": "«from being repainted» — второй детектор правила, приведённый в нём дословно. Чинится точным глаголом или существительным: «to postpone the panel's paint operations» / «to disable repainting».",
"source": "rules/passive-voice.md#how-to-detect-it"
},
{
"id": "pv-neg-01",
"polarity": "must_not_flag",
"trap": "code_fence",
"category": "passive-voice",
"alt_categories": ["weak-verbs", "gerunds"],
"span": "// the active row is repainted automatically after every update",
"line": 18,
"occurrence": 1,
"rationale": "Пассив «is repainted» внутри забора ~~~js — это комментарий в коде, а не проза статьи. Ревью грамматики не редактирует содержимое примеров кода.",
"source": "rules/passive-voice.md#how-to-detect-it"
},
{
"id": "pv-neg-02",
"polarity": "must_not_flag",
"trap": "frontmatter",
"category": "passive-voice",
"alt_categories": ["wordiness", "inversion"],
"span": "You can explore how rows are highlighted in Roster",
"line": 4,
"occurrence": 1,
"rationale": "Пассив «are highlighted» стоит в SEO-шаблоне поля description, одинаковом для всего корпуса. Frontmatter не является прозой статьи, и переписывать шаблон нельзя.",
"source": "rules/passive-voice.md#how-to-detect-it"
},
{
"id": "pv-neg-03",
"polarity": "must_not_flag",
"trap": "rule_exception",
"category": "passive-voice",
"alt_categories": ["weak-verbs", "tenses"],
"span": "fires after the focused row is changed",
"line": 37,
"occurrence": 1,
"rationale": "Правило прямо разрешает этот случай: секция «When passive voice is acceptable» приводит ровно эту фразу («This event fires after the focused row is changed.») как Acceptable, потому что в коротком описании события полный субъект («пользователь кликнул, или контрол перезагрузил данные…») раздул бы summary.",
"source": "rules/passive-voice.md#when-passive-voice-is-acceptable"
},
{
"id": "pv-neg-04",
"polarity": "must_not_flag",
"trap": "correct_prose",
"category": "passive-voice",
"alt_categories": [],
"span": "to read the selected rows after the load completes",
"line": 25,
"occurrence": 1,
"rationale": "«selected» здесь причастие-определение при существительном, а не «to be» + причастие; глаголы «read» и «completes» активны. Пассивной конструкции нет — флагать нечего.",
"source": "rules/passive-voice.md#how-to-detect-it"
},
{
"id": "pv-may-01",
"polarity": "may_flag",
"category": "gerunds",
"alt_categories": [],
"span": "# Row highlighting",
"line": 7,
"occurrence": 1,
"rationale": "Непосеянное нарушение (найдено аудитом). rules/gerunds.md#gerunds-in-titles требует глагольную форму в заголовках: «Data Binding → Bind to Data», «Data Editing → Edit Data». Заголовок H1 «Row highlighting» — герундийное существительное того же вида, ожидаемая правка «Highlight rows». Спан включает «#», чтобы цитата, привязанная к строке 7, не смешивалась с тем же текстом в frontmatter (pv-may-02).",
"source": "rules/gerunds.md#gerunds-in-titles"
},
{
"id": "pv-may-02",
"polarity": "may_flag",
"category": "gerunds",
"alt_categories": [],
"span": "Row highlighting",
"line": 2,
"occurrence": 1,
"rationale": "Непосеянное нарушение (найдено аудитом). Тот же герундийный заголовок стоит в sidebar_label (строка 2) и в title (строка 3: «JavaScript Roster - Row Highlighting» — дословный аналог примера «React Scheduler - Grouping» из rules/gerunds.md#gerunds-in-titles). Цитата без «#» локализуется на первое вхождение, то есть внутрь frontmatter, поэтому запись помечена may_flag: законная находка по герундию в заголовке не должна превращаться в жёсткое зональное ложное срабатывание. Зональную проверку frontmatter продолжает держать pv-neg-02 (строка 4, SEO-описание, спан с ним не пересекается).",
"source": "rules/gerunds.md#gerunds-in-titles"
},
{
"id": "pv-may-03",
"polarity": "may_flag",
"category": "prose-should-be-code",
"alt_categories": ["text-that-explains-ui-or-code", "explains-ui-or-code", "long-sentences"],
"span": "in the left gutter of the Roster control",
"line": 9,
"occurrence": 1,
"rationale": "Непосеянное нарушение (найдено аудитом). Inline-проверка SKILL.md «Text that explains UI or code»: проза описывает положение UI-элемента (левый жёлоб), а такие вещи должны показываться скриншотом или примером кода. Спан начинается после «clicks its handle» и не пересекается с pv-01 (перекрытие 0.13 < порога 0.6).",
"source": "SKILL.md#text-that-explains-ui-or-code"
},
{
"id": "pv-may-04",
"polarity": "may_flag",
"category": "possessives",
"alt_categories": ["wordiness"],
"span": "the `highlight` property of the whole control",
"line": 9,
"occurrence": 1,
"rationale": "Непосеянное нарушение (найдено аудитом). rules/possessives.md, секция «Replace \"of\" with noun phrases where it reads better» («settings of the editor» → «editor settings»): ожидается «the control's `highlight` property», а слово «whole» ничего не добавляет. Спан идёт после двоеточия и не пересекается с pv-02 (перекрытие 0.12 < порога 0.6).",
"source": "rules/possessives.md#replace-of-with-noun-phrases-where-it-reads-better"
},
{
"id": "pv-may-05",
"polarity": "may_flag",
"category": "api-formatting",
"alt_categories": [],
"span": "Set `lockPaint` to true",
"line": 25,
"occurrence": 1,
"rationale": "Непосеянное нарушение (найдено аудитом). Литерал true написан простым текстом, хотя рядом `lockPaint` и `getSelected()` оформлены обратными кавычками, а корпусный образец в rules/articles.md пишет «Set the `ShowPreview` property to `true`». rules/api-formatting.md#what-to-flag требует кавычки для имён и литералов кода в код-контексте. Спан обрывается перед «to prevent» и не пересекается с pv-05 (перекрытие 0.19 < порога 0.6).",
"source": "rules/api-formatting.md#what-to-flag"
}
]
}
Loading