Тяжёлые DOM-блоки row-fill (15–60 строк) вынесены в 8 именованных
функций dom/grid-edit.mjs. row-fill стал плоским orchestrator-ом.
Новое в dom/grid-edit.mjs:
- sortFieldKeysByColindexScript — сортировка keys по colindex (Tab-нав)
- findCellCoordsByFieldsScript — клетка по first matching header (multi)
- findNextCellCoordsByKeyScript — клетка по single key + no-space fuzzy
- findCheckboxAtPointScript — checkbox info по координатам elementFromPoint
- findRowCommitClickCoordsScript — клик OTHER row для commit-edit
- getGridEditCheckScript — { inEdit, tag?, hint? } диагностика
- readActiveGridCellScript — активная клетка (id, fullName, headerText)
- getElementCenterCoordsByIdScript — центр по id (дедуп 2 копий)
Метрики row-fill: 971 → 793 LOC (−178, S6 alone), inline page.evaluate
10 → 1 (значительно ниже плановой цели ≤10). Все табличные suite
зелёные (05/06/08/10/16), полный регресс зелёный (Checkpoint-2).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Новое в dom/grid.mjs (все принимают опциональный gridSelector):
- countGridRowsScript — кол-во .gridLine в body
- isTreeGridScript — тип grid'а (есть .gridBoxTree)
- findGridHeadCenterCoordsScript — центр .gridHead для commit-клика
- getSelectedOrLastRowIndexScript — selected row index, fallback на последний
Также:
- isInputFocusedInGrid wrapper (S1) применён в add-row "ready" поллинге
- isNotInListCloudVisibleScript (S3) применён вместо локального notInList
- clickShowAllInNotInListCloudScript — новая в dom/forms.mjs (клик
"Показать все" в "нет в списке" cloud popup через dispatchEvent)
Метрики row-fill: 1041 → 971 LOC (−70), evaluates 17 → 10. Регресс
05/08/16/10 — зелёный.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Новое в dom/edd.mjs:
- readEddScript — {visible, items:[{name,x,y}]}
- isEddVisibleScript — boolean, лёгкая проверка
- clickEddItemViaDispatchScript — клик по name через dispatchEvent (bypass
div.surface); fuzzy с NBSP/ё/bracket-strip
- clickShowAllInEddScript — "Показать все" в footer
Wrappers в helpers.mjs: readEdd, isEddVisible, clickEddItemViaDispatch,
clickShowAllInEdd. row-fill clickEddItem унифицирован с select-value
вариантом (NBSP/ё normalization теперь работает и для табличных строк).
Метрики: select-value 880 → 827 LOC (−53), row-fill 1065 → 1041 LOC
(−24); evaluates row-fill 20 → 17, select-value 7 → 5. Полный регресс
зелёный.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Все 17 inline page.evaluate в engine/table/filter.mjs вынесены в
именованные dom-генераторы. Engine-модуль стал чистым orchestrator-ом.
Новое в dom/forms.mjs (shared с будущим S3 select-value):
- findSearchInputScript(formNum) — поиск SearchString/ПоискаСтроки input
- findNamedButtonScript(text) — кнопка a.press по innerText (Найти, OK)
- findCompareTypeRadioScript(form, idx) — радио CompareType#N#radio
- isFormVisibleScript(form) — есть ли видимые элементы form{N}
Новое в dom/filter.mjs:
- findFirstGridCellCoordsScript — координаты первой клетки грида
- findColumnFirstCellCoordsScript — клетка по имени колонки (fuzzy header
match с needDlb-fallback)
- readFieldSelectorInfoScript — FieldSelector value + DLB coords
- pickFieldInSelectorDropdownScript — выбор поля в FieldSelector DLB-edd
- readFilterDialogInfoScript — Pattern id+value+isDate+isRef
- findFilterBadgeCloseScript — × badge по имени поля
- findFirstFilterBadgeCloseScript — × первого видимого badge (для clear-all)
Попутно: добавлен импорт readSubmenuScript (был pre-existing broken
import в Еще-fallback ветке Alt+F).
Метрики filter.mjs: 390 → 256 LOC (−134, −34%), inline page.evaluate
17 → 0. Регресс 09-filter / 02-crud / 05-table — зелёный.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SpreadsheetDocument (отчёты, печатные формы) — другой домен, чем form-grid
(табличные части документов, списки). Раньше лежал внутри table/, что
было обманчиво.
engine/table/spreadsheet.mjs → engine/spreadsheet/spreadsheet.mjs
Структура engine/:
core/ плумбинг движка (state, wait, errors, session, click, ...)
forms/ работа с формами (fill, close, select-value, state)
nav/ навигация
table/ form-grid (grid, row-fill, filter, grid-toggle)
spreadsheet/ SpreadsheetDocument
recording/ запись + overlays
В будущем при росте spreadsheet можно распилить — engine/spreadsheet/cells.mjs,
engine/spreadsheet/scroll.mjs и т.д. без переименований.
11-report регресс зелёный.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
getFormState — высокоуровневая операция «прочитать состояние формы»,
семантически в forms/ ближе чем в core/ (foundational плумбинг движка).
engine/core/form-state.mjs → engine/forms/state.mjs
Все 11 importer'ов обновлены. Внутри state.mjs пути исправлены:
'./state.mjs' → '../core/state.mjs', './errors.mjs' → '../core/errors.mjs'.
03-fillfields регресс зелёный.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
После полной чистки cycle-импортов в E.13 остались комментарии типа
"getFormState still in browser.mjs", которые больше не верны (он переехал
в engine/core/form-state.mjs). Сметаем устаревшие "moved to / lives in
browser.mjs" комментарии в 8 файлах.
Дополнительно в engine/table/spreadsheet.mjs:
- убраны неиспользуемые импорты readTableScript, resolveGridScript, normYo
(остались с тех пор, как readTable жил в этом файле — до этапа D.12
rename'а в grid.mjs)
- заголовочный комментарий обновлён (без упоминания readTable)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
readTable читает form-grid (.gridLine/.gridBody — табличные части на форме,
списки), а не SpreadsheetDocument. Имя файла table/spreadsheet.mjs было
обманчиво. Разделяем домены:
table/grid.mjs ← readTable (form-grid операции, готово
для fillTableRow + deleteTableRow в D.12)
table/spreadsheet.mjs ← readSpreadsheet + cell helpers (только
SpreadsheetDocument — отчёты, печатные формы)
Поведение 1-в-1. browser.mjs re-export обновлён.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
clickSpreadsheetCell вызывает getFormState в конце (для drill-down формы),
но import не был добавлен при экстракции на C.11. ReferenceError в
11-report drill-down. Импортируем из browser.mjs (циклически).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
table/spreadsheet.mjs (~580 LOC):
- readTable, readSpreadsheet
- scanSpreadsheetCells, buildSpreadsheetMapping (private)
- scrollSpreadsheetToCell, clickSpreadsheetCell, findSpreadsheetCellByText
table/filter.mjs (~390 LOC): filterList, unfilterList
После C.11 + C.10 clean-up:
- core/click.mjs импортит clickSpreadsheetCell/findSpreadsheetCellByText
напрямую из table/spreadsheet.mjs (а не из browser.mjs)
- browser.mjs больше не реэкспортирует эти два — публичный API
остаётся 56 экспортов как до рефакторинга
- Добавил import { clickElement } в browser.mjs для внутренних вызовов
из fillTableRow/deleteTableRow
browser.mjs: 2470 → 1532 LOC (≈75% от исходных 6293).
05-table + 09-filter регресс зелёный.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
clickElement (~300 LOC) перенесён единым блоком в core/click.mjs.
Поведение 1-в-1. Внутри остаётся всё ветвление (spreadsheet, submenu,
gridGroup/Parent, gridTreeNode, gridRow, tab, button) — разнос на
forms/click-form.mjs + nav/click-popup.mjs + finer table-toggle
отложен на E.13 для безопасности.
clickSpreadsheetCell + findSpreadsheetCellByText временно exported из
browser.mjs (нужны core/click.mjs). На C.11 они переедут в
table/spreadsheet.mjs, экспорт из browser.mjs можно будет убрать.
browser.mjs: 2768 → 2470 LOC.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
forms/fill.mjs (~140 LOC): fillFields, fillField
forms/close.mjs (~50 LOC): closeForm
clickElement остаётся в browser.mjs до C.10.
Допиленные импорты после первого прохода:
- fill.mjs: readFormScript, normYo (из dom/state — забыл при экстракции)
- close.mjs: recorder (используется для паузы 500ms при confirmation
во время записи)
03-fillfields регресс зелёный.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Платформа добавляет SelectedItemAuto и OrderItemAuto в каждую группировку при
ручном создании в конфигураторе. Shorthand-запись (например
'Номенклатура > details') теперь даёт эквивалентный результат — каждая
группа получает selection=['Auto'] и order=['Auto']. Без этого roundtrip
decompile→compile терял авто-элементы.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Перенос selectValue + helpers из browser.mjs (~960 LOC):
- scanGridRows, dblclickAndVerify, advancedSearchInline
- pickFromSelectionForm, isTypeDialog, pickFromTypeDialog (экспортируются —
вызываются из fillFields/fillTableRow в browser.mjs)
- fillReferenceField (экспортируется — вызывается из fillFields)
- selectValue
Двумя слайсами вокруг fillFields/fillField/clickElement/closeForm, которые
остаются в browser.mjs до этапов C.9/C.10.
browser.mjs: 4095 → 2933 LOC. 56 публичных экспортов.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1. В selectValue локальный const detectNewForm = () => ... объявлялся ниже
composite-type ветки, которая его вызывала → TDZ ReferenceError "Cannot
access 'detectNewForm' before initialization". Хелпер поднят в начало
функции, дубликат-объявление убрано.
2. clipboardWarnLogged читается в restoreClipboard (line 92), но не был
в списке импортов из core/state.mjs (импортировался только setter).
ReferenceError срабатывал только когда clipboard.read() возвращал
ошибку — в первом A-регрессе ветка не активировалась случайно.
Регресс 18/19 (одна flake в 11-report — readSpreadsheet timing).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
В clickElement две ветки (gridGroup/gridParent + gridTreeNode) имели
почти идентичные page.evaluate-блоки: найти gridLine под target.y,
получить иконку-разворачивалку, вернуть её центр + isExpanded.
table/grid-toggle.mjs:
- getGridToggleIcon(target, formNum, { iconSelector, isExpandedExpr })
- shouldClickToggle(iconInfo, expand, toggle)
Поведение 1-в-1. Селекторы и isExpanded-критерий передаются параметрами:
- groups: '.gridListH, .gridListV' + icon.classList.contains('gridListV')
- trees: '.gridBoxImg [tree="true"]' + bg.includes('gx=0')
Экономия ~30 LOC дублей.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
core/helpers.mjs: returnFormState(extras) — стандартный хвост action-функций:
getFormState + Object.assign(extras) + checkForErrors → state.errors. Унифицирует
~15 hand-written копий и закрывает R1/R2/R3 (state.errors теперь добавляется
автоматически у любого пользователя хелпера).
В этом коммите конвертированы только 2 простейших P1-сайта (openCommand,
второй handle в navigateLink) — без extras между getFormState и err-проверкой.
Остальные 30+ сайтов сложнее (state.X между, разные return-shape, wrapped
fillFields) — будут мигрированы органически при переносе clickElement/
selectValue/closeForm в forms/* на этапе C.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
В fillReferenceField было два места с одинаковым page.evaluate-скриптом
чтения #editDropDown (DLB-popup перед paste и autocomplete после Ctrl+V).
core/helpers.mjs: readEdd() → { visible, items?: [{ name, x, y }] }.
selectValue использует свой clickEddItem через dispatchEvent (bypass div.surface) —
оставлен как есть, специфика API там сильно отличается.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
В fillReferenceField, selectValue и fillTableRow была одна и та же логика:
сканировать DOM на наличие элемента с id="form{N}_*" где N > prevFormNum.
Две вариации: strict (только visible interactive — input.editInput/a.press)
и broad (любой [id], учитывает type-dialogs с пустыми button-id).
core/helpers.mjs: detectNewForm(prevFormNum, { strict }) → number|null.
Внутри функций оставлены тонкие локальные обёртки (для совместимости
с уже использующейся сигнатурой без аргументов) — будут убраны на C.8/D.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
В selectValue было 4 одинаковых блока поиска input-элемента поля
по имени (form{N}_{name} либо form{N}_{name}_i0 для refs):
clear-ветка, composite-type-ветка, F4-fallback, "last resort" F4.
core/helpers.mjs: findFieldInputId(formNum, fieldName) → string|null.
~30 LOC дублей убрано, поведение 1-в-1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
В fillReferenceField, clickElement и DLB-ветке selectValue был один и тот же
паттерн: page.click → catch 'intercepts pointer events' → force-click →
catch снова → Escape + retry. Три копии (плюс одна с dismissPendingErrors).
core/helpers.mjs (новый): safeClick(selector, { timeout, dismissErrors }).
Экономия ~60 LOC дублей. Поведение 1-в-1 (dismissErrors:true только в
fillReferenceField — там единственное место, где исходно было).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
После A.3 эти helpers стали приватными в core/errors.mjs, но getFormState
(browser.mjs:408) и closeForm (browser.mjs:2168) их по-прежнему вызывают —
ловили ReferenceError на каждое действие. Делаем их экспортируемыми
и импортируем в browser.mjs. Имя с подчёркиванием сохраняется до этапа E.13
(финальная чистка). Регресс 19/19.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Перенос session-функций из browser.mjs (~380 LOC):
- connect, disconnect, attach, detach, getSession
- createContext, setActiveContext, listContexts, getActiveContext,
hasContext, closeContext
- findExtension (приватная)
- _logoutSlot, _saveActiveSlot, _activateSlot, _attachSessionListeners
(приватные multi-context хелперы)
Session-модуль зависит от core/state, core/errors (closeModals),
recording/capture (stopRecording) и циклически от browser.mjs
(getPageState — переедет в nav/navigation.mjs на этапе C.7).
ESM live-binding делает цикл безопасным: getPageState вызывается
только внутри async функций, а не на этапе загрузки модуля.
browser.mjs: 4251 LOC, 56 публичных экспортов. Завершает Чекпоинт A.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
core/wait.mjs (123 LOC):
- waitForStable: smart DOM-stability polling
- waitForCondition: JS-expression polling
- startNetworkMonitor: CDP network-activity monitor
core/errors.mjs (336 LOC):
- closeModals, dismissPendingErrors, checkForErrors, fetchErrorStack
- Платформенные диалоги: _detectPlatformDialogs, _closePlatformDialogs
- _parseErrorStack, _fetchStackViaReport, _fetchStackViaHamburger (приватные)
browser.mjs импортирует их для внутреннего использования и re-export'ит
только fetchErrorStack (исходно публичный). Остальные функции остаются
приватными — публичный API не меняется (56 экспортов).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Состояние движка (browser, page, sessionPrefix, seanceId, recorder, контексты,
константы, normYo, isConnected/ensureConnected/getPage) переехало в
core/state.mjs. Импортируется как live-binding; присваивания в browser.mjs
конвертированы в setX(...) — ESM imports read-only.
Публичный API не меняется (56 экспортов). Регресс 19/19 зелёный.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Тесты активно используют OS clipboard (`writeText` + Ctrl+V — единственный
способ добиться trusted-paste для autocomplete справочников и кириллицы).
При локальном запуске это перетирало пользовательский буфер. Теперь:
- `pasteText(text, {confirm, postDelay})` в browser.mjs делает узкое окно
save → writeText → confirm-key → restore вокруг каждой пасты (~ms).
- Save/restore через `navigator.clipboard.read()`/`write()` — все MIME
(текст, картинка, HTML), blob'ы стэшатся на `window` без CDP-сериализации.
- 14 callsites переведены на helper.
- При failure save'а (CF_HDROP из Проводника не виден через web-API) restore
явно очищает буфер, чтобы тестовое значение не протекало.
- Опт-аут: CLI `--no-preserve-clipboard`, env `WEB_TEST_PRESERVE_CLIPBOARD=0`,
`preserveClipboard: false` в `webtest.config.mjs`.
Регресс tests/web-test — 6 прогонов 19/19 passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
--timeout=<ms> / --timeout-min=<n> и WEB_TEST_EXEC_TIMEOUT_MS вместо
захардкоженных 30 мин; сообщение об ошибке строится из фактического
значения. Закрывает кейс длинных записей видео с addNarration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Дополняем гайд группы skd-*:
- В таблицу навыков добавлена строка /skd-decompile с пометкой об
отключённом автоподборе моделью.
- В блок «Рабочий цикл» нарисована обратная стрелка Template.xml →
/skd-decompile → JSON DSL.
- Новый под-раздел «Когда /skd-decompile, а когда /skd-edit» с явным
предупреждением о неполноте преобразования и тихих потерях.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- description короче и с явным «не для точечных правок»
- унифицируем терминологию (sentinel-узлы)
- расширяем «Когда не использовать» — почему /skd-edit лучше для адресных правок
- в критичные конструкции добавляем вложенные схемы
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Два бага PS-версии, незаметно роняли 9/30 отчётов sample30 в decompile-fail:
1. Get-Text без xpath → SelectSingleNode("", $ns) с .NET XPathException
"Результатом выражения должен быть NodeSet". Шесть call-sites в
dataSetLinks (sourceDataSet/destinationDataSet/sourceExpression/...)
передавали уже-выбранный узел без второго аргумента; [string]$xpath
дефолтился в "". Фикс: Get-Text возвращает $node.InnerText, если
xpath пустой.
2. $paramByName[$startMatch] при $startMatch=$null → "индекс массива
вычислен как NULL". Возникает на StandardPeriod-параметре, для
которого в отчёте нет companion expressions. Фикс: guard через if.
Python-порт #2 уже был защищён .get(); по #1 в py был обходной костыль
inline-через-inner_text — заменён на единый get_text(node) после
обновления сигнатуры до get_text(node, xpath=None).
verify-roundtrip sample30: 9 bit-perfect + 20 with-diff + 1 ring3 = 30
(до фикса 9 silently падали как decompile-fail, сумма не сходилась).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- value параметра может быть массивом (для valueListAllowed)
- расшифровка namespace-ов цветов: style: (палитра темы), web: (web-имена),
win: (Windows-системные)
Compile имеет map outputParamTypes — для известных ключей (Заголовок,
ВыводитьПараметрыДанных, РасположениеИтогов и др.) тип эмитится
автоматически по имени параметра. Decompile же всегда оборачивал в
wrapper {value, valueType} для не-xs:* типов — лишний шум.
Зеркалирован тот же map в decompile (15 keys), при чтении проверяем
совпадение fullType с map → если auto-detect compile вернёт тот же
тип, валидируем как простое значение.
JSON outputParameters стал заметно компактнее. Эффект на diff
нейтральный (compile эмитит то же).
Для filter right values с типом dcscor:DesignTimeValue, чьё значение
соответствует ref-pattern (Перечисление.X.Y / Справочник.X.Y / ПланСчетов.X.Y
и др.), compile auto-detect-ит DesignTimeValue из значения. Раньше
decompile всё равно сохранял valueType явно — лишний шум в DSL.
Применено к single-right и multi-right. Эффект на diff нулевой (compile
эмитит то же самое), но decompiled JSON становится компактнее и читабельнее.
Раньше эмитили шапку схемы в 8 строк (каждый xmlns на отдельной строке).
Оригинал платформы пишет всё в одну строку. Был с самого начала
существования skd-compile — не регрессия, но косметика на каждый отчёт
давала 9 строк diff (1 LOST + 8 ADDED).
Sample30 total: 458 → 238 строк diff.