- Парсинг <template>/<template xsi:type=AreaTemplate>/<dcsat:TableRow>/
<dcsat:tableCell> в rows[][].
- Распознавание содержимого ячейки: dcsat:Text → строка, dcsat:Field с
dcscor:Parameter → "{Имя}", dcsat:Field с LocalStringType → строка/multilang,
пустая → null.
- Merge через appearance-флаги ОбъединятьПоВертикали/ОбъединятьПоГоризонтали
на пустых ячейках → "|"/">".
- Детект built-in стилей (header/data/subheader/total) через нормализованный
fingerprint appearance — без сравнения per-cell ширин/высот/merge-флагов.
При несовпадении или неоднородности — sentinel TemplateStyleMismatch.
- Извлечение widths из appearance первого row и minHeight из первой ячейки.
- Drilldown-свёртка: для cells с appearance Расшифровка=Расшифровка_X
и template-параметром DetailsAreaTemplateParameter Расшифровка_X →
свертываем в `{name, expression, drilldown: X}`.
- Сохранение порядка template parameters через [ordered]@{}.
- Fix namespace URI для areatemplate (`area-template` с дефисом).
Bit-perfect round-trip 55924→55924 и 28590→28590 на синтетике с header/data
стилями, merge, drilldown, шаблонными параметрами.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- calculatedFields: shorthand с [title], type, expression и #restrict-флагами;
object form при appearance или multilang title.
- totalFields: детект Func(name) и Func(expr) → shorthand "name: Func"/"name: Func(expr)";
object form при привязке к группе.
- parameters:
- shorthand с [title], type, value, @-флагами;
- распознавание StandardPeriod variants → значение в shorthand;
- @valueList, @hidden флаги;
- availableValues с presentation;
- object form для availableValues/multilang/composite type/expression.
- autoDates-сворачивание: для каждого StandardPeriod-параметра ищем пару
dependent с expression `&P.ДатаНачала`/`&P.ДатаОкончания` (распознаём по
expression, не по имени) и сворачиваем в @autoDates на родителе.
- decimal-тип всегда эмитится с явными (D,F) — JSON читаемее.
- useRestriction суппрессим в параметрах (auto-generated для @hidden).
Bit-perfect round-trip 7468→7468 байт на синтетике
(3 calc + 2 total + 5 параметров включая @autoDates).
Реальный ERP «АнализИзмененийЛичныхДанныхСотрудников» (1035 строк) —
0 warnings при декомпиляции.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Распознавание типов (string/decimal/boolean/date/dateTime/time/CatalogRef.X
и пр.) с qualifiers (decimal-точность/знак, string Length/AllowedLength,
date fractions) → shorthand или composite-массив.
- Роли (@dimension/@account/@balance/@period) с детектом сложных roleAttributes
как sentinel.
- Restrictions (#noField/#noFilter/#noGroup/#noOrder) из useRestriction.
- Multilingual title с авто-сворачиванием {ru:"..."} в строку.
- appearance с поддержкой LocalStringType значений (например, Формат).
- presentationExpression.
- Свёртка дефолтного dataSource (ИсточникДанных1/Local) в умолчание.
- Автодетект object vs shorthand формы поля.
Bit-perfect round-trip на синтетике из 11 разнотипных полей.
Реальный ERP-отчёт АнализВерсийОбъектов декомпилируется с 0 warnings.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Убрал ложное обещание «структурной эквивалентности» (DSL покрывает
подмножество СКД). Слил «Гарантии» и «Не поддерживается» в раздел
«Что получаешь» с тремя категориями (покрытое / sentinel / fail-fast).
Добавил Workflow — декомпил это начало процесса, а не финал.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Layer 1 of the skd-decompile plan: SKILL.md with disable-model-invocation,
ps1 skeleton with XML→JSON pipeline, namespace probe for non-DCS root,
sentinel/warnings accumulator, and DataSetQuery extraction (query only).
Test case minimal-query demonstrates round-trip via skd-compile preRun.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Добавляет одну строку-подсказку в add-field: для попадания в Selection
конкретной группировки (а не variant) — связка -NoSelection + add-selection
с @group=. Это уже работало, но не было явно зафиксировано в SKILL.md.
Расширять сам add-field параметром -Group/@group= не стали — текущий
двухкомандный идиом более атомарен и не создаёт edge cases вроде
взаимодействия @group= и -NoSelection.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Раньше PS1-порт делал `Join-Path (Get-Location) $OutFile` без проверки,
что приводило к невалидным склейкам типа `C:\cwd\C:\abs\path.txt`, и
запись падала с «The given path's format is not supported».
Теперь: если путь абсолютный — нормализуется через `Path::GetFullPath`,
если относительный — резолвится против CWD. Python-порт уже был корректен,
только version bump.
Дополнительно: `args_extra` в runner.mjs теперь поддерживает подстановку
`{workDir}` — нужно для тестов с абсолютными путями внутри workspace.
Тесты: `skd-info/outfile-absolute-cyrillic` (PS + Python).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
patch-query теперь нормализует CRLF/CR → LF в old/new/query перед поиском,
поэтому многострочные шаблоны с любым стилем переводов строк находятся
корректно (XmlDocument декодирует text-узлы как LF).
При not-found вместо сухого сообщения выводится воронка диагностики:
1) cross-dataset probe — «Found in dataset 'Y' instead — wrong -DataSet?»
2) tolerant probe (collapse whitespace + NBSP) — «would match with
whitespace normalized» + точка расхождения
3) prefix divergence — «matched N of M chars, expected 'X' (U+...) but
got 'Y' (U+...)» + короткий контекст
Тесты: 4 новых кейса (positive CRLF-tolerant + 3 диагностических negative).
Регрессия 45/45 PS + 45/45 Python.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Calibrated against live Designer output in upload/erf/ПроверкаЭкранирования.
- New type 'time' (synonym 'время'): xs:dateTime with DateFractions=Time
for time-of-day values. Designer uses the same xs:dateTime XSD type as
date/dateTime — only DateFractions differs. Empty value: typed-zero
0001-01-01T00:00:00 (same as dateTime).
- Extended string regex to accept (N,fix) → AllowedLength=Fixed (was
Variable-only). Non-empty fixed-string values are emitted as-given
without space-padding to Length — the platform handles padding on save.
- Composite types in parameters (array of types in object form, e.g.
["string(10,fix)", "CatalogRef.X"]) now work end-to-end: valueType
emits each type with its qualifiers, and empty composite values
serialize as <value xsi:nil="true"/> matching Designer.
Test case empty-param-values extended with 5 new params covering all
three additions. Snapshot validated by skd-validate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Calibrated against 1106 vendor reports (ERP 8.3.24 + БП 8.3.27).
Three categories of false positive removed:
- CalculatedField with empty <expression/> demoted error→warning.
Three legitimate vendor patterns surfaced:
* sibling totalField with same dataPath provides the formula
(used in cancellation-rate and share-percentage reports)
* groupTemplate references the field as group name
* field exists only as a declarative anchor for settingsVariants
Warning preserved so genuinely-missing formulas still surface.
- Duplicate template name demoted error→warning. Vendor configs ship
reports (БазаНормируемыхРасходов/Выручка) with three <template> blocks
named Макет1 — the platform identifies templates by position, not by
<name>. Warning still flags the collision without failing validation.
- comparisonType whitelist extended with NotInHierarchy and
NotInListByHierarchy. Existing list was missing the negated
hierarchy operators used in 20 of the 1106 reports.
Result: 0 false positives across the corpus, all genuine errors still
caught (verified separately against intentionally-broken fixtures).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Calibrated against ~868 real ERP/БП reports — three false positives caught:
1. Composite types: <v8:Type>xs:string</v8:Type> followed by
<v8:Type>d4p1:CatalogRef.X</v8:Type> with a single trailing
<v8:StringQualifiers> is a legitimate pattern. Rewritten check to
collect all <v8:Type> and qualifier blocks per <valueType>, then
verify each qualifier has a matching scalar type anywhere in the
block — not necessarily right before it.
2. System types: AccumulationRecordType (and similar enum-like system
types) use the http://v8.1c.ru/8.1/data/enterprise namespace
(without /current-config) and a plain TypeName local name with no
dot. Whitelisted as a second valid namespace for ref-like types.
3. v8: scalar types extended: v8:Null, v8:Type, v8:ValueStorage —
present in real configs as type-less placeholders.
Also reverted SKILL.md change from previous commit (validator details
don't belong in user-facing docs).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
skd-validate was purely structural (names/refs/duplicates) and missed an
entire class of bugs that XDTO rejects at db-load-xml — exactly the
kinds of mistakes the LLM (or hand-edits) commonly introduce.
New section 16: valueType structural — each <v8:Type> must have a known
prefix (xs:/v8: or any prefix bound to enterprise/current-config),
qualifier blocks must match their preceding type, and qualifier
internals (Digits/FractionDigits/AllowedSign, Length/AllowedLength,
DateFractions) must use legal tokens.
New section 17: value content — <value xsi:type="dcscor:DesignTimeValue">
rejects literal placeholders ('_') and empty strings, since these are
the exact symptom of the titan team's BUG-2.
5 new fixtures cover: bare-decimal, missing-qualifiers,
qualifier/type mismatch, ref-literal '_', bad AllowedSign token.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Emit-SingleValueType / emit_single_value_type previously required full
decimal(D,F) — anything else fell through to a fallback that produced
invalid <v8:Type>decimal</v8:Type> (no xs: prefix, no qualifiers).
New regex `^decimal(\((\d+)(,(\d+))?(,nonneg)?\))?$` accepts:
- decimal → 10,2,Any (money default — most common 1C intent)
- decimal(N) → N,0,Any (integer)
- decimal(N,nonneg) → N,0,Nonnegative
- decimal(N,M) → as before
- decimal(N,M,nonneg) → as before
Synonyms (число, число(N), etc.) inherit the same forms via Resolve-TypeStr.
Shared Emit-ValueType is called from fields, parameters, and output
parameters — one fix covers all three paths. 3 existing snapshots
regenerated with proper xs:decimal + qualifiers, plus new
decimal-qualifier-defaults test case covering all 5 forms × synonyms.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Зеркалим решение из skd-compile: убираем .Replace('"','"') из Esc-Xml
и удаляем post-process, который принудительно ставил " внутри
<query>/<expression>. Реальный Конфигуратор так не пишет — экранирование
было анти-1С-стилем и портило round-trip diff.
Снимок add-calculated-field-restrict обновлён под новый формат.
Кейс preserve-entities-modify-parameter-title удалён: его смысл
инвертировался (теперь проверял бы нормализацию, а не сохранение),
а часть про многострочный xmlns уже покрыта preserve-xmlns-multiline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Конфигуратор внутри текстового контента <query>/<expression> оставляет " сырыми
(проверено на ERP DCS: 1504 raw " против 0 "). Убираем .Replace('"','"')
из esc_xml — теперь round-trip diff против типовых остаётся чистым.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
multilang-base/Template.xml содержал <editFormat xsi:type="v8:LocalStringType">
на <field xsi:type="DataSetFieldField">, что нелегально по XDTO-схеме DCS —
1С Designer падал с "Исключение XDTO" при загрузке через
LoadExternalDataProcessorOrReportFromFiles. Snapshot-тесты этого не ловили
(только byte-equality), а platform-verify (tests/skills/verify-snapshots.mjs)
ронялся на трёх кейсах с этой фикстурой.
Заменил <editFormat> на реалистичный <appearance> блок с вложенным
<dcscor:item xsi:type="dcsset:SettingsParameterValue"> и многоязычным
<dcscor:value> (ru + en) — структура взята из типовой ERP-выгрузки. Это
даёт более правильный test для preserve-unknown-children: <appearance>
содержит вложенный multi-lang xsi:type-узел, который точно прошёл бы
через DOM round-trip с искажениями, если бы _unknownChildren не работал.
preserve-unknown-children-modify-field: shorthand изменён с
"@ignoreNullsInGroups" на "@dimension" (no-op по составу role, но
триггерит rebuild). Прежний @ignoreNullsInGroups без @dimension давал
комбинацию, которую Designer отвергает (ignoreNullsInGroups валиден
только в контексте resource-роли).
39/39 snapshot suite (PS+PY) + 39/39 platform verify через erf-build →
Designer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
В типовых конфигурациях (ERP, БП, ЗУП и т.д.) у полей и параметров обычно
есть мульти-язык title (ru + en, иногда + локализация). До этого modify-field /
modify-parameter / modify-dataParameter, перестраивая элемент через
Build-MLTextXml, оставляли только последнее найденное <v8:content> в ru —
en/uk/kk siblings молча терялись, и при следующей выгрузке Designer
ломал миграцию.
Read-FieldProperties сохраняет полный OuterXml <title> в _rawTitle и
коллекционирует OuterXml неизвестных дочерних элементов
(<editFormat>, <appearance>, кастомные расширения) в _unknownChildren.
Build-FieldFragment эмитит:
* _rawTitle как есть, если user не задал новый title;
* Patch-MLTextRu(_rawTitle, newRu) если user задал ru-override — патчит
только <v8:content> в <v8:lang>ru</v8:lang>, остальные языки сохраняет;
* _unknownChildren в конце поля (после valueType).
modify-parameter аналогично: при title-override проверяет multi-lang
(>1 <v8:item>) и патчит ru через Patch-MLTextRu, иначе ребилдит ru-only.
set-field-role сохраняет нестандартные подэлементы <role> (например
<dcscom:addition>, <dcscom:groupFields>), не входящие в фиксированный
known-children set и не указанные через kv в shorthand.
xmlns-стрип на захваченных OuterXml — лишние декларации (которые сериализаторы
добавляют для standalone-фрагментов) убираются.
PY: lxml etree.tostring по умолчанию включает .tail (whitespace после
закрывающего тега), что приводило к non-idempotent ростy whitespace при
повторных прогонах. Везде добавлен with_tail=False.
Новые тесты с idempotent: true:
* preserve-multilang-modify-field (ru-override на multi-lang title);
* preserve-multilang-modify-parameter (то же для параметра);
* preserve-unknown-children-modify-field (role flag, проверяем что
<editFormat> и en title не теряются).
Общая fixture: multilang-base/Template.xml с полем и параметром,
у каждого ru + en title; поле также имеет <editFormat>.
39/39 PS + 39/39 PY. skd-edit v1.20 -> v1.21.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Targeted follow-ups к round-trip фиксу:
* modify-field больше не теряет <valueType> при перестройке поля —
Read-FieldProperties сохраняет полный OuterXml элемента (StringQualifiers,
NumberQualifiers, DateQualifiers и т.п.), Build-FieldFragment отдаёт его
обратно. Лишние xmlns-декларации, добавляемые сериализатором при
выгрузке поддерева, стрипаются регексом.
* Line-ending convention теперь определяется при load (CRLF vs LF) и
единообразно применяется в финале save. Раньше CreateWhitespace и
Build-*Fragment везде использовали CRLF, что приводило к смешанным
переносам в LF-исходниках (и наоборот) и к non-idempotent выходу
modify-parameter title (run 1 → \n\t\t<title>\r\n... → run 2 →
\r\n\t\t<title>\r\n...).
* PS Insert-BeforeElement переведён на LF; все -join "`r`n" → "`n";
py "\r\n".join → "\n". Конечная нормализация переносов делается в
save в соответствии со script:LineEnding.
* preserve-entities-modify-parameter-title.json теперь idempotent: true
(после фикса CRLF leak'а двойной прогон byte-identical).
На реальной схеме diff после modify-field составил 30 строк: целевая
вставка title плюс полезная одноразовая коррекция ранее повреждённых
" в text-content <dcsat:expression>. modify-field идемпотентен.
skd-edit v1.19 -> v1.20.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
XmlDocument round-trip искажал Template.xml даже при отсутствии правок:
декодировал " в <query>/<expression>, схлопывал многострочный xmlns
корня, добавлял пробел перед /> и записывал файл при [WARN] not found.
Дирти-флаг ($script:Dirty / dirty) ставится только на успешной мутации;
финальный save пропускается с [INFO] No changes -- file untouched, если
ни одна операция в batch ничего не изменила. Post-process после OuterXml
восстанавливает raw-форматирование корневого xmlns из исходного файла,
re-escape `"` в текстах <query>/<expression> с anchored regex (не задевая
xsi:type="..."), и нормализует <foo .../> к <foo.../>.
Замеры на реальной схеме после modify-field: diff упал с 423 строк до 37
(94% шума устранено), повторный прогон byte-identical.
В runner.mjs добавлен caseData.idempotent: re-run + byte-equality на всех
файлах workDir. Три новых кейса (NO-OP, entity-preserve, xmlns-multiline)
+ общий fixture roundtrip-base. Все 33 ранее существовавших snapshot
перегенерированы под корректное форматирование (восстанавливают то, что
старый skd-edit ломал).
skd-edit v1.18 -> v1.19. PS и PY порты синхронизированы.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Новый канонический документ docs/web-test-regression-spec.md —
техническое описание движка регрессионных тестов: CLI, формат
тест-модулей, ctx-контракт, утверждения, три уровня хуков
(инфра/тест/контекст), конфиг, контексты Playwright и режимы
изоляции, форматы отчётов (JSON/Allure/JUnit), обнаружение тестов,
ошибки/таймауты/повторы, анализ результатов, глоссарий.
Документ предназначен для CI-интеграторов, ручного редактирования
сгенерированных тестов и сопровождения самого движка. Без дорожной
карты и внутренних self-тестов — только публичный контракт.
regress.md в скилле почищен: добавлены контракт ctx и список
утверждений (раньше модели приходилось читать исходники), срезаны
дубликаты с SKILL.md (live recon, паттерны catalog/document),
переформулированы анти-паттерны под специфику регресс-движка.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
В тест-обёртке ACTION_FN при 1С-исключении на throw-ed Error
вешалась полная структура (step, args, errors, formState, stack,
screenshot), но при сборке отчёта движок брал из неё только
{message, step, screenshot} — остальные поля терялись. Платформенный
стек 1С, ради которого делается fetchErrorStack, в JSON-отчёт не
попадал; в Allure statusDetails.trace писался только log()-вывод
теста.
Что поменялось:
- errInfo собирается один раз после teardown (раньше был дубликат на
732 и 745), используется и для ctx.testResult (afterEach), и для
lastError, и для итоговой записи в results[].
- В errInfo добавлено поле onecError: e.onecError — структура с
stack.entries[{location, code}], formState, args, errors доезжает
до JSON-отчёта без обрезания.
- writeAllure склеивает statusDetails.trace из tr.output + (если есть)
onecError.stack.raw под разделителем "--- 1C stack ---". В Allure UI
платформенный стек теперь виден прямо в карточке упавшего теста.
Обратная совместимость: для падений без 1С-исключения (assertion,
навигация и т.п.) e.onecError === undefined → JSON.stringify его
выкидывает, форма записи { message, screenshot } сохраняется в
точности.
Проверено вручную на стенде tests/web-test/ — падающий тест с
ВызватьИсключение, JSON и Allure оба содержат полный stack.entries и
формированный trace.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Новая операция clear-conditionalAppearance в стиле clear-selection/
order/filter. Закрывает потребность "заменить набор правил оформления"
через clear + re-add.
- patch-query: многострочные подстроки уже работали (string.Replace
корректно обрабатывает \n). Зафиксировано в SKILL.md.
- add-total: shorthand-шаблон с тремя случаями (Func, Func(expr),
identity-выражение) — после fix Bug 6 поведение нужно явно объяснить.
- Косметика: убрана утечка XML-внутренностей в комментарии примера
set-field-role @period.
- Пример patch-query @once заменён на более типовой случай уникальной
подстроки (КАК ВТ_СтароеИмя вместо ЛЕВОЕ СОЕДИНЕНИЕ).
Регресс: 33/33 PS, 33/33 PY, 33/33 платформенный verify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Раньше "DataPath: X" всегда заворачивалось в X(DataPath). Если X не
аггрегатная функция (например, имя другого ресурса или сам DataPath),
получалось некорректное выражение типа Проверка(Проверка).
Зеркалю логику из skd-compile: whitelist аггрегатных функций
(Сумма, Количество, Минимум, Максимум, Среднее + EN-варианты).
Для остального — identity (использовать funcPart как есть).
Сообщение [OK] теперь показывает фактически записанный expression.
Регресс: 32/32 PS, 32/32 PY, 32/32 платформенный verify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Защищает от случайных замен в комментариях/совпадениях имён:
"ЛЕВОЕ СОЕДИНЕНИЕ => ВНУТРЕННЕЕ СОЕДИНЕНИЕ @once"
# fail, если в запросе 0 или 2+ вхождений
Без флага default — replace-all (как раньше, обратная совместимость).
При успехе сообщение содержит фактическое число вхождений
"(N occurrence(s))", помогает заметить неожиданную множественность
без явного @once.
Регресс: 31/31 PS, 31/31 PY, 31/31 платформенный verify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Единый list-синтаксис: availableValue=v1[: p1], v2[: p2], ...
Элементы через запятую, представление после двоеточия.
- Запятые/двоеточия внутри значений и представлений — в одинарных кавычках:
availableValue=Окр1: 'руб., коп.', Окр1000: руб.
- add-parameter теперь принимает availableValue= и создаёт начальный список
в одном вызове (раньше требовался последующий modify-parameter).
- modify-parameter availableValue=... ЗАМЕНЯЕТ весь список (раньше
append). Согласуется с остальными modify-* для одиночных свойств.
- SKILL.md: добавлен shorthand-шаблон для modify-parameter,
расширен для add-parameter [availableValue=список].
Существующие тесты мигрированы со старого ;;-batch на новый list-синтаксис.
Снапшоты сохранились (тесты стартовали с пустого списка — semantics
совпадает для greenfield).
Регресс: 29/29 PS, 29/29 PY, 29/29 платформенный verify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
В -Mode fields -Name <field> к сводке Role добавляются не-bool
параметры роли (balanceGroupName, balanceType, parentDimension,
accountTypeExpression и т.д.) в формате name=value.
Bool-флаги (@balance, @dimension, ...) отображаются как раньше.
False-значения по-прежнему скрыты.
Регресс: 6/6 PS, 6/6 PY (существующие snapshots не задеты).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- @hidden — скрывает параметр от пользовательских настроек
(useRestriction=true + availableAsField=false). Для констант-параметров.
- @always — параметр всегда подставляется в запрос (use=Always).
Используется самостоятельно для видимых обязательных параметров.
- Композируются: @hidden @always одной строкой даёт типовой паттерн
"скрытая константа всегда применяется".
- Поддержка в add-parameter и modify-parameter, идемпотентны.
Регресс: 25/25 PS, 25/25 PY, 25/25 платформенный verify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
В реальных выгрузках ERP/БП встречаются непрозрачные платформенные
DataPath, которые невозможно проверить из одного Form.xml:
- bare numeric ("10", "1000003") — внутренние индексы платформы
- "N/M:<uuid>" — ссылка на метаданные по UUID
Раньше Check 5 ругался на них "attribute not found". Теперь такие
пути пропускаются без счёта в paths checked и без ошибки.
Реалистичные пользовательские опечатки (кириллица в имени атрибута)
продолжают ловиться обычной проверкой attrMap.
Добавлен тест-кейс datapath-opaque-refs, версия v1.5 → v1.6.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
В Check 5 раньше брался первый сегмент DataPath и искался в attrMap,
из-за чего ложно ругались реальные формы ERP/БП с путями вида
Items.<TableName>.CurrentData.<Field> (подвалы, инфо-панели) и
~<DynamicListAttr>.<Field> (текущая строка списка).
Теперь:
- ведущий ~ стрипается перед разбором сегментов;
- для Items.<Table>.CurrentData.* находим элемент-таблицу по name,
берём её <DataPath> (атрибут DynamicList/TableSection) и проверяем
его в attrMap. Если таблицы нет — Error; если третий сегмент не
CurrentData — Warn.
Добавлен тест-кейс datapath-currentdata, версия скриптов v1.4 → v1.5.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Добавлена строка в таблицу навыков и в перечень docs/ для нового
docs/web-test-regression-guide.md (был забыт при первичном коммите
регресс-гайда).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- docs/web-test-regression-guide.md — пользовательские сценарии работы
с моделью для покрытия прикладного решения регрессом (русский, по
аналогии с web-test-recording-guide.md): структура tests/<app-name>/,
диалоги с моделью, пример организации покрытия, отчёты Allure +
categories.json.
- .claude/skills/web-test/regress.md — инструкция модели по написанию
регрессионного набора: разведка (метаданные + живой проход через exec),
layout по фичам, готовые шаблоны (CRUD/document/DCS/multi-user/repro),
severity, anti-patterns, failure triage, _allure/ конвенция.
- SKILL.md — указатель на regress.md в конце файла (рядом с recording).
- docs/web-test-runner-spec.md → upload/ (был внутренним планом
разработки, не пользовательской документацией).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
run.mjs:
- syncAllureExtras(testDir, reportDir) копирует все файлы из
<testDir>/_allure/ в reportDir перед генерацией отчёта. Underscore
в имени параллелен _hooks.mjs (инфра, не тест) — discovery его
пропускает.
- Вызов после writeAllure при --format=allure.
tests/web-test/_allure/categories.json — 7 правил классификации падений
по нашему 1С-домену:
1. License pool exhausted (1C) — известный multi-context flake.
2. 1C application error (modal) — exception modal через fetchErrorStack.
3. Section panel icon-only — деградация состояния стенда.
4. Navigation lookup miss — navigateSection/openCommand/navigateLink/switchTab.
5. Element not found — clickElement/fillField/selectValue/closeForm/fillTableRow/deleteTableRow.
6. Test timeout — Timeout (Nms) от раннера.
7. Assertion failure — наши createAssertions + 1С-specific (formHasField/tableHasRow/noErrors).
spec §9: раздел «Доп. файлы Allure через <testDir>/_allure/» с таблицей
поддерживаемых типов (categories.json / environment.properties /
executor.json) и минимальным примером.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
run.mjs:
- buildSeverityIndex(config) — валидация config.severity (inverted map
«уровень → [теги]») при загрузке: ключи только из blocker|critical|
normal|minor|trivial, теги не дублируются между bucket'ами,
defaultSeverity тоже валидируется. fail-fast через die.
- resolveSeverity(t, severityIndex):
1. mod.severity если задан и валидный — выигрывает.
2. max-rank среди тегов (стандартные имена severity или маппинг).
3. config.defaultSeverity или 'normal'.
Rank: blocker(5) > critical(4) > normal(3) > minor(2) > trivial(1).
Max-wins инвариантен к порядку тегов.
- writeAllure: добавлены labels suite (= dirname(t.file) или 'root') +
severity. Тег `tag` остался как раньше.
- testResult пробрасывает t.severity (для passed/failed веток).
- SEVERITY_RANK/LEVELS объявлены в модульной шапке (top-level await на
cmdTest начинается до конца тела модуля, TDZ-аккуратность).
webtest.config.mjs: severity policy для нашего сьюта (smoke +
multi-context → critical, recording → minor, defaultSeverity = normal).
spec.md §7: раздел про severity-policy в конфиге с валидацией.
spec.md §9: «Авто-эмиссия label-ов» — tag/suite/severity + правила резолва.
Регресс 19/19 ✓ (9m 7.6s). Распределение по уровням после исправления
'record' → 'recording' в маппинге: 13 critical / 5 normal / 1 minor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Зафиксирована конвенция:
- Discovery рекурсивный, путь попадает в отчёт.
- Per-folder hooks/config/context-default НЕ поддерживаются (by design).
- Группировку в отчётах делать через tags, не через путь.
- Сортировка по полному пути (`warehouse/01-x` после `sales/02-y`) —
для глобального порядка нужны 3-значные префиксы или теги-фазы.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
browser.mjs:
- + closeContext(name): logout slot + close page (tab) или context (window),
удаление из реестра. Throw если name неактивен (рулило: nicht den aktiven
closen, recorder always attached к active → invariant простой).
- _logoutSlot(slot, waitMs) — извлечён из disconnect, переиспользуется в
closeContext.
run.mjs:
- ensureContext() после createContext вызывает hooks.afterOpenContext(ctx, name, spec).
- wrapCloseContextHook() оборачивает ctx.closeContext (и каждую scoped-обёртку)
чтобы перед browser.closeContext fir'ить hooks.beforeCloseContext.
- Финальный teardown в finally: для всех живых контекстов кроме первого
(survivor) — beforeCloseContext + closeContext; для survivor только хук,
его закрывает disconnect().
_hooks.mjs v0.5:
- afterOpenContext инжектит persistent DOM-badge с displayName в правый
верхний угол page — в записанном видео всегда видно, какой контекст.
- beforeCloseContext counter-only.
- _state расширен полями afterOpenContext / beforeCloseContext.
15-multi-context-handover.test.mjs:
- +2 шага: closeContext('b') после handover, попытка closeContext(active)
ловится throw'ом с проверкой message.
00-hooks.test.mjs:
- +1 ассерт: afterOpenContext >= 1 (default уже создан), beforeCloseContext === 0
в теле первого теста.
spec §6:
- Раздел «Контекстный уровень» (afterOpenContext / beforeCloseContext + правила closeContext).
- ASCII-диаграмма порядка хуков обновлена с per-context lifecycle.
Регресс 19/19 ✓ (9m 16.8s).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
_hooks.mjs v0.4: beforeEach под условием ctx.isRecording() показывает
title slide с testInfo.name + displayName первичного контекста как
subtitle, ждёт 1.5с через ctx.wait() и убирает.
В обычном регрессе (без --record) — ветка скипается, overhead ноль.
Под --record: 01-navigation 12.1s → 13.9s (+1.8с на слайд).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13-misc.test.mjs: setup-шаг упрощён до `assert.ok(existsSync(epfPath))`.
EPF-сборку (epf-init → form-add → form-compile → epf-build) забрал
_hooks.mjs.prepare() — здесь только проверка артефакта с понятной
ошибкой при отсутствии: «запустите раннер с `-- --rebuild-epf`».
webtest.config.mjs: URL обоих контекстов переключён на
`/webtest-runner/ru_RU` — отдельная публикация автономного стенда,
не конфликтует с интерактивной разведкой через `/webtest` на 8081.
Новый tests/web-test/_hooks.mjs v0.2 с prepare()/cleanup().
prepare() поднимает изолированный стенд:
- Hash-locks `tests/skills/.cache/webtest-stand/{config,epf}.lock`
на sha256 от build-steps и EPF_SPEC — автоматический skip
пересборки при отсутствии изменений.
- Слои конфиг XML / БД / EPF пересобираются независимо. Триггер
ручной — флаги `--rebuild-config`/`--reload-data`/`--rebuild-epf`/
`--rebuild-stand` (через `-- ...` после CLI раннера).
- Smart Apache: web-stop+web-publish выполняются только когда
пересоздаём БД (нужно освободить блокировку). Иначе probe-first:
жив (200) → no-op; мёртв → publish + probeReady. На warm-старте
prepare сводится к чтению локов и одному probe (~200ms).
- web-publish на собственном AppName `webtest-runner` :9191 — не
пересекается с интерактивной публикацией `webtest`.
- Кросс-платформенно: env WEBTEST_HOOKS_RUNTIME=python переключает
на зеркальные py-порты скиллов (для не-Windows стендов).
cleanup() пока stub — оставляем стенд поднятым между прогонами,
для full-shutdown ручной /web-stop или `-- --rebuild-stand`.
E2E-проверено: cold-start `--rebuild-stand` поднимает стенд за
~38s; warm-старт prepare = 0.0s; полный регресс 18/18 зелёный
за 9m 7.1s (включая оба multi-context-теста, которые исторически
флапали).
В CLI раннера всё после `--` собирается в массив hookArgs и
передаётся в инфра-хуки prepare/cleanup без интерпретации со
стороны раннера. Сигнатура расширена до { hookArgs, log, config }:
log — структурированный вывод раннера, config — разобранный
webtest.config.mjs. Шаблон «всё после `--` принадлежит вложенному
инструменту» — стандартная shell-конвенция (npm, cargo, pytest).
Спека §6 обновлена под новую сигнатуру, §6.1 закрепляет контракт
`--` ↔ hookArgs с примером. Help-строка раннера упоминает
разделитель.
Извлечены exports: getProjectInfo, resolveScript, execSkill,
replacePlaceholders, runSteps, platformLoadSteps, loadBuildSteps.
CLI-режим сохранён через import.meta.url-guard. Подготовка к
переиспользованию из tests/web-test/_hooks.mjs без дублирования
exec-логики и pipeline-шагов.
Раньше шаг был deferred с комментарием «требует список с видимой
filter-панелью». На самом деле существующая абстракция работает:
два advanced filterList на разных колонках Контрагентов создают
два badge'а в state.filters[], а unfilterList({field}) снимает
конкретный — оставляя остальные.
Новый шаг 09-filter/unfilter-specific (~14s):
- filterList('ООО', {field:'Наименование'}) + filterList('123', {field:'ИНН'})
→ state.filters = [{field:'Наименование',value:'ООО'}, {field:'ИНН',value:'123'}]
- unfilterList({field:'ИНН'}) → остался только Наименование badge
- unfilterList() → пусто
Старый комментарий «defer to filter-panel synthetic» удалён —
оказался устаревшим (видимо unfilterList({field}) уже умел работать
с advanced-filter badge'ами на синтетических списках).
timeout 09-filter поднят с 60000 → 120000ms (8 шагов теперь, +14s
для unfilter-specific).
Регресс: 16/18 зелёных. Два multi-context-теста (14/15) упали на
лицензионном пределе 1С — known environmental issue, не связано с
этим коммитом.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>