fix(cfe-patch-method): ключ сравнения контроля — по измеренному правилу платформы

-Check считал дрейфом расхождение по пустым строкам (шумный ложный ДРЕЙФ, из-за
которого -Actualize предлагал косметическую перезапись тела) и НЕ считал дрейфом
расхождение по пробелам между токенами (тихий ложный АКТУАЛЕН — платформа такой
метод молча не применяет, он рвётся при первом вызове).

Правило платформы измерено на стенде: 27 вариантов косметики, платформы 8.3.24 и
8.3.27, общий модуль и модуль объекта, два независимых оракула
(/CheckCanApplyConfigurationExtensions и рантайм-вызов метода) — результат везде
одинаковый. Каждая строка Trim, строки, пустые после Trim, из сравнения выброшены,
всё остальное байт в байт и с учётом регистра: значимы пробелы между токенами,
пробелы внутри строкового литерала, комментарии целиком и регистр.

Вердикт АКТУАЛЕН считается по этому правилу (Get-ControlKey / control_key); в PS
сравнение ordinal, иначе -eq игнорирует регистр и порт расходится с Python.
Эвристики слияния (якоря, поглощение, дифф) остались на прежнем мягком normalize.

Кейсы: допуски (пустые строки, отступ, хвостовые пробелы) и два негатива —
пробелы между токенами и регистр идентификатора.

Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
This commit is contained in:
Nick Shirokov
2026-08-30 18:29:14 +03:00
co-authored by Sergei Pleshanov Claude Opus 5
parent 4fa57c73b5
commit 533dd0ded4
16 changed files with 641 additions and 5 deletions
+1 -1
View File
@@ -88,7 +88,7 @@ allowed-tools:
Правила:
- Маркеры — на **отдельной строке с 0-й колонки** (без отступа), даже внутри отступов и текста запроса (`|…`).
- **Незамеченные (unmarked) строки должны совпадать с оригиналом дословно** — это и есть «контроль». Если оригинал в конфигурации-источнике изменится, unmarked-контекст разойдётся → метод потребует актуализации (см. ниже; проверить пачкой — `-Check`).
- **Незамеченные (unmarked) строки должны совпадать с оригиналом дословно** — это и есть «контроль». Дословно — включая комментарии, регистр и пробелы внутри строки (`Х = Х + 1``Х=Х+1`); свободны только отступ и пустые строки. Если оригинал в конфигурации-источнике изменится, unmarked-контекст разойдётся → метод потребует актуализации (см. ниже; проверить пачкой — `-Check`).
- Меняешь только свои `#Вставка`/`#Удаление`; чужой оригинал не трогай.
## Актуализация
@@ -1,4 +1,4 @@
# cfe-patch-method v2.9 — Source-aware method interceptor for 1C extension (CFE)
# cfe-patch-method v2.10 — Source-aware method interceptor for 1C extension (CFE)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
[CmdletBinding(PositionalBinding=$false)]
param(
@@ -362,6 +362,14 @@ function Get-Normalized {
return (($line -replace '\s+', ' ').Trim())
}
# Control comparison key, as the platform compares a &ИзменениеИКонтроль copy with the original:
# each line trimmed, blank lines dropped, everything else byte-for-byte and case-sensitive
# (inner spaces, comments and letter case are significant). Measured on 8.3.24 and 8.3.27.
function Get-ControlKey {
param($lines)
return (@($lines | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' }) -join "`n")
}
# Reconstruct v1 body and edit ops from a marked body
function Parse-MarkedBody {
param($bodyLines)
@@ -651,7 +659,7 @@ function Invoke-Resync {
$v1norm = @($v1 | ForEach-Object { Get-Normalized $_ })
$v2norm = @($v2 | ForEach-Object { Get-Normalized $_ })
if (($v1norm -join "`n") -eq ($v2norm -join "`n")) {
if ([string]::Equals((Get-ControlKey $v1), (Get-ControlKey $v2), 'Ordinal')) {
return @{ Id = $methodId; Status = 'АКТУАЛЕН'; ExtBsl = $extBsl }
}
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# cfe-patch-method v2.9 — Source-aware method interceptor for 1C extension (CFE)
# cfe-patch-method v2.10 — Source-aware method interceptor for 1C extension (CFE)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -491,6 +491,13 @@ def normalize(line):
return re.sub(r'\s+', ' ', line).strip()
# Control comparison key, as the platform compares a &ИзменениеИКонтроль copy with the original:
# each line trimmed, blank lines dropped, everything else byte-for-byte and case-sensitive
# (inner spaces, comments and letter case are significant). Measured on 8.3.24 and 8.3.27.
def control_key(lines):
return "\n".join([k for k in (x.strip() for x in lines) if k != ""])
def parse_marked_body(body_lines):
v1 = []
ops = []
@@ -1146,7 +1153,7 @@ def resync_one(ext_bsl, ext_lines, dup, method, logical_module, conflict_folder,
v1norm = [normalize(x) for x in v1]
v2norm = [normalize(x) for x in v2]
if "\n".join(v1norm) == "\n".join(v2norm):
if control_key(v1) == control_key(v2):
return {"id": method_id, "status": "АКТУАЛЕН", "ext_bsl": ext_bsl}
insert_top = []; insert_after = {}; del_start = set(); del_end = set(); disputed = []; transferred = 0; absorbed = 0; absorbed_notes = []