fix(support-guard): не блокировать автономные внешние обработки/отчёты (#39)

При поиске корня конфигурации guard поднимался по дереву вверх и «проскакивал»
собственный корень автономной внешней обработки/отчёта (ExternalDataProcessor /
ExternalReport), лежащей внутри дерева выгрузки конфигурации. Если у охватывающей
конфигурации выключена возможность изменения (G=1), внешний объект ложно
блокировался как «объект типовой конфигурации на поддержке», а info-навыки
выводили нерелевантную строку «Поддержка: конфигурация read-only».

Теперь climb останавливается на границе автономного объекта: если целевой файл или
встреченный по пути <каталог>.xml имеет корень ExternalDataProcessor/ExternalReport,
подъём прекращается и объект не привязывается к конфигурации. Корень внешнего объекта
всегда глубже Configuration.xml, поэтому встречается первым — регрессии для обычных
объектов конфигурации нет.

Синхронно во всех копиях guard-а (навыки автономны): хук support-state.mjs
(decideSupport + findConfigRoot), 16 мутаторов (Assert-EditAllowed), 5 info-навыков
и meta-info (Get-SupportStatusForPath / Get-ObjectSupportStatus) — ps1 и py. Для
info-навыков строка «Поддержка:» для внешнего объекта опускается.

Тесты: hooks/test/run.mjs — секция внешней границы (G=1 + встроенная EPF);
tests/skills — кейсы mxl-compile (guard пропускает) и mxl-info (строка опущена).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-17 19:46:54 +03:00
co-authored by Claude Opus 4.8
parent e7b5df4d50
commit ddc1641176
64 changed files with 1400 additions and 58 deletions
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses"><Configuration uuid="11111111-1111-1111-1111-111111111111"><Properties><Name>ТестКонфа</Name></Properties></Configuration></MetaDataObject>
@@ -0,0 +1 @@
{6,1,1,aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,0,bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb,"1.0","ТестВендор","ТестКонфа",1,0,0,cccccccc-cccc-cccc-cccc-cccccccccccc}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses">
<ExternalDataProcessor uuid="99999999-9999-9999-9999-999999999999"><Properties><Name>Демо</Name></Properties></ExternalDataProcessor>
</MetaDataObject>
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>1</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<i>0</i>
<c>
<f>2</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Значение</v8:content>
</v8:item>
</tl>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>1</defaultFormatIndex>
<height>1</height>
<vgRows>1</vgRows>
<namedItem xsi:type="NamedItemCells">
<name>Ячейка</name>
<area>
<type>Rows</type>
<beginRow>0</beginRow>
<endRow>0</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<font faceName="Arial" height="10" bold="false" italic="false" underline="false" strikeout="false" kind="Absolute" scale="100"/>
<format>
<width>10</width>
</format>
<format>
<font>0</font>
<fillType>Text</fillType>
</format>
</document>
@@ -0,0 +1,23 @@
{
"name": "Guard #39: автономная EPF внутри конфигурации на поддержке (G=1) НЕ блокируется",
"setup": "fixture:epf-in-locked-config",
"input": {
"columns": 2,
"areas": [
{
"name": "О",
"rows": [
{
"cells": [
{
"col": 1,
"text": "a"
}
]
}
]
}
]
},
"outputPath": "print-forms/Демо/Templates/Макет/Ext/Template.xml"
}
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses"><Configuration uuid="11111111-1111-1111-1111-111111111111"><Properties><Name>ТестКонфа</Name></Properties></Configuration></MetaDataObject>
@@ -0,0 +1 @@
{6,1,1,aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,0,bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb,"1.0","ТестВендор","ТестКонфа",1,0,0,cccccccc-cccc-cccc-cccc-cccccccccccc}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses">
<ExternalDataProcessor uuid="99999999-9999-9999-9999-999999999999"><Properties><Name>Демо</Name></Properties></ExternalDataProcessor>
</MetaDataObject>
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>2</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<i>0</i>
<c>
<f>2</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>a</v8:content>
</v8:item>
</tl>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>1</defaultFormatIndex>
<height>1</height>
<vgRows>1</vgRows>
<namedItem xsi:type="NamedItemCells">
<name>О</name>
<area>
<type>Rows</type>
<beginRow>0</beginRow>
<endRow>0</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<font faceName="Arial" height="10" bold="false" italic="false" underline="false" strikeout="false" kind="Absolute" scale="100"/>
<format>
<width>10</width>
</format>
<format>
<font>0</font>
<fillType>Text</fillType>
</format>
</document>
@@ -0,0 +1,6 @@
{
"name": "Info #39: для автономной EPF строка «Поддержка:» не выводится",
"setup": "fixture:epf-in-locked-config",
"params": { "templatePath": "print-forms/Демо/Templates/Макет/Ext/Template.xml" },
"expect": { "stdoutNotContains": "Поддержка:" }
}
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses"><Configuration uuid="11111111-1111-1111-1111-111111111111"><Properties><Name>ТестКонфа</Name></Properties></Configuration></MetaDataObject>
@@ -0,0 +1 @@
{6,1,1,aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,0,bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb,"1.0","ТестВендор","ТестКонфа",1,0,0,cccccccc-cccc-cccc-cccc-cccccccccccc}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses">
<ExternalDataProcessor uuid="99999999-9999-9999-9999-999999999999"><Properties><Name>Демо</Name></Properties></ExternalDataProcessor>
</MetaDataObject>
@@ -0,0 +1,228 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>5</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<i>0</i>
<c>
<f>2</f>
<parameter>ТекстЗаголовка</parameter>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>1</index>
<row>
<c>
<i>0</i>
<c>
<f>3</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Поставщик:</v8:content>
</v8:item>
</tl>
</c>
</c>
<c>
<i>2</i>
<c>
<f>2</f>
<parameter>ПредставлениеПоставщика</parameter>
<detailParameter>Поставщик</detailParameter>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>2</index>
<row>
<c>
<i>0</i>
<c>
<f>5</f>
<parameter>НомерСтроки</parameter>
</c>
</c>
<c>
<i>1</i>
<c>
<f>5</f>
<parameter>Товар</parameter>
<detailParameter>Номенклатура</detailParameter>
</c>
</c>
<c>
<i>2</i>
<c>
<f>6</f>
<parameter>Количество</parameter>
</c>
</c>
<c>
<i>3</i>
<c>
<f>6</f>
<parameter>Цена</parameter>
</c>
</c>
<c>
<i>4</i>
<c>
<f>6</f>
<parameter>Сумма</parameter>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>3</index>
<row>
<c>
<i>3</i>
<c>
<f>7</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Итого:</v8:content>
</v8:item>
</tl>
</c>
</c>
<c>
<i>4</i>
<c>
<f>8</f>
<parameter>Всего</parameter>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>1</defaultFormatIndex>
<height>4</height>
<vgRows>4</vgRows>
<merge>
<r>0</r>
<c>0</c>
<w>4</w>
</merge>
<merge>
<r>1</r>
<c>0</c>
<w>1</w>
</merge>
<merge>
<r>1</r>
<c>2</c>
<w>2</w>
</merge>
<namedItem xsi:type="NamedItemCells">
<name>Заголовок</name>
<area>
<type>Rows</type>
<beginRow>0</beginRow>
<endRow>0</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<namedItem xsi:type="NamedItemCells">
<name>Поставщик</name>
<area>
<type>Rows</type>
<beginRow>1</beginRow>
<endRow>1</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<namedItem xsi:type="NamedItemCells">
<name>Строка</name>
<area>
<type>Rows</type>
<beginRow>2</beginRow>
<endRow>2</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<namedItem xsi:type="NamedItemCells">
<name>Итого</name>
<area>
<type>Rows</type>
<beginRow>3</beginRow>
<endRow>3</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<line width="1" gap="false">
<v8ui:style xsi:type="v8ui:SpreadsheetDocumentCellLineType">Solid</v8ui:style>
</line>
<font faceName="Arial" height="10" bold="false" italic="false" underline="false" strikeout="false" kind="Absolute" scale="100"/>
<font faceName="Arial" height="10" bold="true" italic="false" underline="false" strikeout="false" kind="Absolute" scale="100"/>
<format>
<width>10</width>
</format>
<format>
<font>0</font>
<fillType>Parameter</fillType>
</format>
<format>
<font>0</font>
<fillType>Text</fillType>
</format>
<format>
<font>0</font>
<leftBorder>0</leftBorder>
<topBorder>0</topBorder>
<rightBorder>0</rightBorder>
<bottomBorder>0</bottomBorder>
</format>
<format>
<font>0</font>
<leftBorder>0</leftBorder>
<topBorder>0</topBorder>
<rightBorder>0</rightBorder>
<bottomBorder>0</bottomBorder>
<fillType>Parameter</fillType>
</format>
<format>
<font>0</font>
<leftBorder>0</leftBorder>
<topBorder>0</topBorder>
<rightBorder>0</rightBorder>
<bottomBorder>0</bottomBorder>
<horizontalAlignment>Right</horizontalAlignment>
<fillType>Parameter</fillType>
</format>
<format>
<font>1</font>
<topBorder>0</topBorder>
<horizontalAlignment>Right</horizontalAlignment>
<fillType>Text</fillType>
</format>
<format>
<font>1</font>
<topBorder>0</topBorder>
<horizontalAlignment>Right</horizontalAlignment>
<fillType>Parameter</fillType>
</format>
</document>
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses"><Configuration uuid="11111111-1111-1111-1111-111111111111"><Properties><Name>ТестКонфа</Name></Properties></Configuration></MetaDataObject>
@@ -0,0 +1 @@
{6,1,1,aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,0,bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb,"1.0","ТестВендор","ТестКонфа",1,0,0,cccccccc-cccc-cccc-cccc-cccccccccccc}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses">
<ExternalDataProcessor uuid="99999999-9999-9999-9999-999999999999"><Properties><Name>Демо</Name></Properties></ExternalDataProcessor>
</MetaDataObject>
@@ -0,0 +1,228 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>5</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<i>0</i>
<c>
<f>2</f>
<parameter>ТекстЗаголовка</parameter>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>1</index>
<row>
<c>
<i>0</i>
<c>
<f>3</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Поставщик:</v8:content>
</v8:item>
</tl>
</c>
</c>
<c>
<i>2</i>
<c>
<f>2</f>
<parameter>ПредставлениеПоставщика</parameter>
<detailParameter>Поставщик</detailParameter>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>2</index>
<row>
<c>
<i>0</i>
<c>
<f>5</f>
<parameter>НомерСтроки</parameter>
</c>
</c>
<c>
<i>1</i>
<c>
<f>5</f>
<parameter>Товар</parameter>
<detailParameter>Номенклатура</detailParameter>
</c>
</c>
<c>
<i>2</i>
<c>
<f>6</f>
<parameter>Количество</parameter>
</c>
</c>
<c>
<i>3</i>
<c>
<f>6</f>
<parameter>Цена</parameter>
</c>
</c>
<c>
<i>4</i>
<c>
<f>6</f>
<parameter>Сумма</parameter>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>3</index>
<row>
<c>
<i>3</i>
<c>
<f>7</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Итого:</v8:content>
</v8:item>
</tl>
</c>
</c>
<c>
<i>4</i>
<c>
<f>8</f>
<parameter>Всего</parameter>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>1</defaultFormatIndex>
<height>4</height>
<vgRows>4</vgRows>
<merge>
<r>0</r>
<c>0</c>
<w>4</w>
</merge>
<merge>
<r>1</r>
<c>0</c>
<w>1</w>
</merge>
<merge>
<r>1</r>
<c>2</c>
<w>2</w>
</merge>
<namedItem xsi:type="NamedItemCells">
<name>Заголовок</name>
<area>
<type>Rows</type>
<beginRow>0</beginRow>
<endRow>0</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<namedItem xsi:type="NamedItemCells">
<name>Поставщик</name>
<area>
<type>Rows</type>
<beginRow>1</beginRow>
<endRow>1</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<namedItem xsi:type="NamedItemCells">
<name>Строка</name>
<area>
<type>Rows</type>
<beginRow>2</beginRow>
<endRow>2</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<namedItem xsi:type="NamedItemCells">
<name>Итого</name>
<area>
<type>Rows</type>
<beginRow>3</beginRow>
<endRow>3</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<line width="1" gap="false">
<v8ui:style xsi:type="v8ui:SpreadsheetDocumentCellLineType">Solid</v8ui:style>
</line>
<font faceName="Arial" height="10" bold="false" italic="false" underline="false" strikeout="false" kind="Absolute" scale="100"/>
<font faceName="Arial" height="10" bold="true" italic="false" underline="false" strikeout="false" kind="Absolute" scale="100"/>
<format>
<width>10</width>
</format>
<format>
<font>0</font>
<fillType>Parameter</fillType>
</format>
<format>
<font>0</font>
<fillType>Text</fillType>
</format>
<format>
<font>0</font>
<leftBorder>0</leftBorder>
<topBorder>0</topBorder>
<rightBorder>0</rightBorder>
<bottomBorder>0</bottomBorder>
</format>
<format>
<font>0</font>
<leftBorder>0</leftBorder>
<topBorder>0</topBorder>
<rightBorder>0</rightBorder>
<bottomBorder>0</bottomBorder>
<fillType>Parameter</fillType>
</format>
<format>
<font>0</font>
<leftBorder>0</leftBorder>
<topBorder>0</topBorder>
<rightBorder>0</rightBorder>
<bottomBorder>0</bottomBorder>
<horizontalAlignment>Right</horizontalAlignment>
<fillType>Parameter</fillType>
</format>
<format>
<font>1</font>
<topBorder>0</topBorder>
<horizontalAlignment>Right</horizontalAlignment>
<fillType>Text</fillType>
</format>
<format>
<font>1</font>
<topBorder>0</topBorder>
<horizontalAlignment>Right</horizontalAlignment>
<fillType>Parameter</fillType>
</format>
</document>