Files
cc-1c-skills/docs/v8-project-guide.md
T
c7b0dce151 feat(skills): порядок объектов метаданных в ChildObjects
Навыки-создатели дописывали новый объект в конец группы своего вида, а стандарт
требует порядка по имени (АПК:1108). Замеры на 8 боевых выгрузках: 103 385 объектов
лежат по алфавиту, нарушают его ровно дописки в хвост. Стенд подтвердил, что
беспорядок вечен: платформа нормализует порядок ВИДОВ (возвращает канонический),
но порядок имён внутри вида не трогает.

Настройка newObjectPosition в .v8-project.json (end по умолчанию | byName) —
решение проекта, а не вызова: читают её meta-compile, role-compile, xdto-compile,
cfe-borrow и cf-edit add-childObject. Компаратор при этом константа, моделирующая
дерево Конфигуратора: ключ «ранг+символ» без культурных таблиц, одинаковый в обоих
портах на любой ОС. На корпусе он даёт 4 нарушения на 125 088 пар против 2866 у
ordinal-сравнения, которым cf-edit и cfe-borrow сортировали до сих пор.

Subsystem не упорядочивается автоматически нигде: пока подсистемы не перечислены
в <SubsystemsOrder>, порядок дерева задаёт порядок разделов в панели, а платформа
этот список сама не заводит.

Настройка не чинит накопленное, поэтому cf-edit получил операцию sort-childObjects
(вся конфигурация или названные виды). Она переставляет значения узлов, а не узлы,
поэтому диф — чистая перестановка строк. Имя вида принимается в любом регистре,
во множественном числе и по-русски; неизвестный вид — отказ со списком допустимых.

Попутно:
- PS-порты создателей переведены с DOM-сериализации на текстовую вставку: на
  выгрузке не в каноне Конфигуратора они переписывали заголовок без просьбы;
- PS-сторона семьи detect_xml_style/finalize_xml_bytes закрыта в cf-edit и
  cfe-borrow — правка чужого файла наследует его BOM/EOL/заголовок;
- xdto-compile присоединён к семье Register-InChildObjects вместо инлайн-копии;
- закрыт ложный успех в py subsystem-compile: <ChildObjects /> с пробелом ET
  разбирает, а текстовые ветки не находили — файл писался без вставки;
- cfe-borrow py не импортировал json, из-за чего резолвер молча возвращал end;
- cf-edit add-childObject ставил объект в конец блока, за пределы своей группы,
  когда видов старше в файле не было.

Проверки: 906/906 на PowerShell и 903/906 на Python, 10 гардов, верификация
эталонов платформой без падений, раундтрип отсортированной конфигурации на
8.3.24 и 8.3.27, сортировка боевой выгрузки ACC (1,4 МБ) и расширения из корпуса.

Задачу принёс PR #85; часть кода взята оттуда.

Co-Authored-By: Sergei Pleshanov <72200277+Abacadabras@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoAJmoNbgWKobA7JGgN5S3
2026-08-29 17:34:09 +03:00

365 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Конфигурация проекта (.v8-project.json)
Файл `.v8-project.json` — единый конфиг проекта для всех навыков Claude Code. Хранит пути к платформе 1С, список баз данных и настройки инструментов (Apache, ffmpeg, TTS).
Размещается в корне проекта (рядом с `.git/`). Создаётся навыком `/db-list add` или вручную.
> **Шаблон**: в корне репозитория лежит `.v8-project.example.json` — скопируйте его в `.v8-project.json` и поправьте пути под свою машину.
> **Безопасность**: файл содержит секреты (пароли баз данных, API-ключи TTS) и добавлен в `.gitignore` — он не попадает в репозиторий. Каждый разработчик заводит свой `.v8-project.json` локально. Пример (`.v8-project.example.json`) секретов не содержит и хранится в репозитории.
## Полная схема
```jsonc
{
// === Платформа ===
"v8path": "C:\\Program Files\\1cv8\\8.3.24.1691\\bin",
// === Базы данных ===
"databases": [
{
"id": "dev", // уникальный идентификатор
"name": "Разработка", // отображаемое имя
"type": "file", // "file" или "server"
"path": "C:\\Bases\\MyApp_Dev", // каталог (для file)
"user": "Admin", // пользователь 1С
"password": "", // пароль
"aliases": ["dev", "разработка"], // альтернативные имена
"branches": ["dev", "feature/*"], // привязка к Git-веткам
"configSrc": "src\\cf", // каталог XML-выгрузки конфигурации (см. структуру ниже)
"webUrl": "http://localhost:8081/dev", // URL веб-клиента (для /web-test)
// Хранилище конфигурации (для /db-repo). Без него навыки группы db-* не смогут
// работать с базой, подключённой к хранилищу: платформа не примет от них ни одной
// операции конфигуратора.
"repository": {
"path": "\\\\srv01\\repo\\MyApp", // каталог или tcp://srv01:1542/MyApp
"user": "Ivanov",
"password": ""
},
// Расширения конфигурации: каталог исходников и, если есть, СВОЁ хранилище
"extensions": [
{
"name": "МоёРасширение",
"src": "src\\cfe\\МоёРасширение",
"repository": { "path": "\\\\srv01\\repo\\MyApp_Ext", "user": "Ivanov", "password": "" }
}
]
},
{
"id": "test",
"name": "Тестовая",
"type": "server", // серверная база
"server": "srv01", // адрес сервера 1С
"ref": "MyApp_Test", // имя базы на сервере
"user": "Admin",
"password": "123",
"aliases": ["test", "тест"]
}
],
"default": "dev",
// === Инструменты ===
"webPath": "C:\\tools\\apache24", // каталог Apache
"ffmpegPath": "C:\\tools\\ffmpeg\\bin\\ffmpeg.exe", // путь к ffmpeg
"tts": { // настройки озвучки
"provider": "edge",
"voice": "ru-RU-DmitryNeural"
}
}
```
## Корневые поля
| Поле | Тип | Обяз. | По умолчанию | Описание | Кто заполняет |
|------|-----|:-----:|-------------|----------|---------------|
| `v8path` | string | да | — | Путь к каталогу `bin` платформы 1С (или к файлу `1cv8.exe`/`ibcmd.exe`, см. ниже) | `/db-list add` или руками |
| `v8args` | array | нет | `[]` | Дополнительные аргументы запуска `1cv8.exe` для всех навыков (см. ниже) | Руками |
| `ibcmdargs` | array | нет | `[]` | То же для `ibcmd`, в форме `--ключ=значение` | Руками |
| `databases` | array | да | — | Список баз данных | `/db-list add` |
| `default` | string | нет | — | `id` базы по умолчанию | `/db-list` |
| `editingAllowedCheck` | `"deny"`/`"warn"`/`"off"` | нет | `deny` | Глобальная реакция support-guard на правку объектов на замке (см. ниже) | Руками |
| `newObjectPosition` | `"end"`/`"byName"` | нет | `end` | Куда навыки ставят новый объект в `<ChildObjects>` (см. ниже) | Руками |
| `skillSuggester` | `"on"`/`"off"` | нет | `on` | Подсказки навыков от хука skill-suggester (только если хук включён, см. ниже) | Руками |
| `webPath` | string | нет | `tools/apache24` | Каталог Apache HTTP Server | Руками |
| `ffmpegPath` | string | нет | `tools/ffmpeg/bin/ffmpeg.exe` | Путь к ffmpeg | Руками |
| `tts` | object | нет | Edge TTS, DmitryNeural | Настройки озвучки видео | Руками |
## Базы данных (`databases[]`)
| Поле | Тип | Обяз. | Описание | Кто заполняет |
|------|-----|:-----:|----------|---------------|
| `id` | string | да | Уникальный идентификатор | `/db-list add` |
| `name` | string | да | Отображаемое имя | `/db-list add` |
| `type` | `"file"` / `"server"` | да | Тип подключения | `/db-list add` |
| `path` | string | для file | Каталог файловой базы | `/db-list add` |
| `server` | string | для server | Адрес сервера 1С | `/db-list add` |
| `ref` | string | для server | Имя базы на сервере | `/db-list add` |
| `user` | string | нет | Пользователь 1С | `/db-list add` или руками |
| `password` | string | нет | Пароль | `/db-list add` или руками |
| `aliases` | string[] | нет | Альтернативные имена для обращения к базе | `/db-list add` или руками |
| `branches` | string[] | нет | Git-ветки или glob-паттерны (`release/*`, `feature/*`) | Руками |
| `configSrc` | string | нет | Каталог XML-выгрузки конфигурации (рекомендуется `src/cf`, см. структуру ниже). Путь относительный — от корня проекта | Руками |
| `editingAllowedCheck` | `"deny"`/`"warn"`/`"off"` | нет | Override реакции support-guard для этой базы (см. ниже) | Руками |
| `newObjectPosition` | `"end"`/`"byName"` | нет | Override места вставки в `<ChildObjects>` для этой базы (см. ниже) | Руками |
| `skillSuggester` | `"on"`/`"off"` | нет | Override подсказок навыков для этой базы (см. ниже) | Руками |
| `webUrl` | string | нет | URL веб-клиента для `/web-test` | Руками |
| `repository` | object | нет | Хранилище конфигурации: `path`, `user`, `password` (см. ниже) | Руками |
| `extensions` | array | нет | Расширения конфигурации: `name`, `src`, необязательное `repository` (см. ниже) | Руками |
### Хранилище конфигурации (`repository`, `extensions[]`)
База, подключённая к хранилищу конфигурации, не принимает **ни одной** операции конфигуратора без
реквизитов доступа к хранилищу — это касается не только `/db-repo`, но и `/db-load-xml`,
`/db-dump-xml`, `/db-update`, `/db-load-git`. Реквизиты берутся из `repository` записи базы, поэтому
передавать их в каждом вызове не нужно.
| Поле | Тип | Обяз. | Описание |
|------|-----|:-----:|----------|
| `repository.path` | string | да | Каталог хранилища или `tcp://<хост>[:<порт>]/<имя>` |
| `repository.user` | string | нет | Пользователь хранилища. **Не наследуется** от `user` базы — задаётся явно |
| `repository.password` | string | нет | Пароль пользователя хранилища |
У **расширения своё хранилище** со своим путём — одного `repository` на запись базы недостаточно:
| Поле | Тип | Обяз. | Описание |
|------|-----|:-----:|----------|
| `extensions[].name` | string | да | Имя расширения, как в конфигурации |
| `extensions[].src` | string | нет | Каталог XML-исходников расширения (напр. `src/cfe/<Имя>`) |
| `extensions[].repository` | object | нет | Хранилище расширения. Расширение без хранилища — обычный случай |
Пароль хранилища — такой же секрет, как `password` базы; `.v8-project.json` в `.gitignore`.
> **Сетевое хранилище.** Адрес — `tcp://<хост>[:<порт>]/<имя>`, порт по умолчанию 1542.
> Обслуживается сервером хранилища (`crserver`); версия сервера должна совпадать с версией
> платформы. Если сервер недоступен, платформа отвечает «Соединение с хранилищем конфигурации не
> установлено» — тем же сообщением, что и при отсутствии реквизитов, поэтому навыки различают эти
> случаи по виду адреса.
`/db-repo update` отказывается работать, если у базы не объявлено `repository` и реквизиты не
переданы явно: на базе, **не** подключённой к хранилищу, эта команда молча заменяет всю
конфигурацию содержимым хранилища и рапортует успех.
### Support-guard и `editingAllowedCheck`
Навыки-мутаторы (`meta-edit`, `meta-compile`, `meta-remove` и др.) перед изменением исходников проверяют состояние поддержки конфигурации (`Ext/ParentConfigurations.bin`, см. [1c-support-state-spec.md](1c-support-state-spec.md)). Если объект «на замке» поставщика (или вся конфигурация read-only, или удаляется не снятый с поддержки объект), правка по умолчанию **блокируется** — прямое изменение сломало бы обновления.
Реакцию задаёт `editingAllowedCheck`:
- `deny` (по умолчанию, в т.ч. когда поле не задано) — блокировать с диагностикой;
- `warn` — пропускать, но писать предупреждение;
- `off` — проверку не выполнять.
Триггер проверки — наличие `ParentConfigurations.bin` (конфигурация на поддержке), а не регистрация в `.v8-project.json`. Поле лишь меняет реакцию. Берётся `databases[].editingAllowedCheck` базы, чей `configSrc` охватывает редактируемый путь; иначе — корневое `editingAllowedCheck`; иначе `deny`.
### Порядок объектов метаданных и `newObjectPosition`
Объекты верхнего уровня перечислены в `<ChildObjects>` файла `Configuration.xml` — сначала группами по видам, внутри вида по одному на строку. Поле задаёт, куда навык ставит **новую** запись внутри своего вида:
- `end` (по умолчанию, в том числе когда поле не задано) — после последнего объекта того же вида, как дописывает Конфигуратор;
- `byName` — по имени, как требует стандарт разработки для объектов верхнего уровня (АПК:1108 «Нарушена сортировка объектов метаданных верхнего уровня по имени по возрастанию в дереве метаданных», #std467 п. 2.3).
Читают поле навыки, добавляющие запись в состав: `meta-compile`, `role-compile`, `xdto-compile`, `cfe-borrow` и операция `add-childObject` навыка `cf-edit`. Раскладка та же, что у `editingAllowedCheck`: берётся `databases[].newObjectPosition` базы, чей `configSrc` (относительно каталога `.v8-project.json`) охватывает каталог правимого файла; иначе корневое поле; иначе `end`.
Сам файл `.v8-project.json` ищется вверх **от каталога конфигурации**, и лишь потом от текущего каталога — в отличие от `editingAllowedCheck`, который смотрит сначала на текущий каталог. Настройка принадлежит выгрузке: навык почти всегда вызывают из другого проекта, и его `.v8-project.json` перекрыл бы нужный.
**Порядок имён** — как в дереве Конфигуратора: регистр не учитывается, подчёркивание раньше цифр, цифры раньше букв, латиница раньше кириллицы, `ё` на месте `е`. Сравнение реализовано ключом «ранг+символ», а не культурными таблицами ОС, поэтому PowerShell- и Python-порты дают одинаковый результат на любой платформе.
**Подсистемы поле не затрагивает** — они всегда дописываются в конец. Порядок подсистем в дереве задаёт порядок разделов в панели, пока они не перечислены в `<SubsystemsOrder>` файла `Ext/CommandInterface.xml`; сама платформа этот список не заводит, а `subsystem-compile` его не трогает (это работа `interface-edit`). Сортировка подсистем поэтому переставляла бы разделы интерфейса молча.
**Что поле не делает.** Оно влияет только на новые записи и не приводит в порядок уже накопленное — для этого есть `/cf-edit -Operation sort-childObjects`. `byName` к тому же предполагает, что список уже упорядочен: объект встаёт перед первым объектом своего вида, чьё имя больше. Взаимный порядок групп видов навыки не меняют вовсе: платформа приводит его к своему при первой же выгрузке.
**Виды с осмысленным порядком.** В типовых конфигурациях `CommandGroup`, `PaletteColor` и `Language` перечислены не по алфавиту — порядок там выбран разработчиком. Стандарт распространяется и на них, так что `byName` и сортировка этот порядок перебьют; на работу конфигурации это не влияет (проверено загрузкой и обратной выгрузкой), но диф будет.
### Хуки и `skillSuggester` (экспериментально)
Помимо встроенной в навыки проверки (выше), есть **опциональные хуки Claude Code** (каталог `hooks/`), которые по умолчанию **выключены** и подключаются вручную (см. `hooks/README.md`):
- **support-guard** — перехватывает правки исходников на поддержке **в обход навыков** (прямые `Edit`/`Write`); реакцию берёт из того же `editingAllowedCheck`;
- **skill-suggester** — ненавязчиво подсказывает профильный навык, когда модель работает с исходниками напрямую.
`skillSuggester` (`on`/`off`, по умолчанию `on`) включает/выключает подсказки skill-suggester. Действует только когда хук подключён; раскладка та же — `databases[].skillSuggester` для базы по `configSrc`, иначе корневое, иначе `on`.
### Разрешение базы
Все навыки `/db-*`, `/epf-build`, `/epf-dump`, `/erf-build`, `/erf-dump`, `/web-publish` используют единый алгоритм:
1. Если пользователь указал **параметры подключения** (путь, сервер) — используются напрямую
2. Если указал **базу по имени** — поиск: `id``aliases` (с учётом морфологии) → `name` (нечёткое)
3. Если **не указал** — сопоставление текущей ветки Git с `branches` (точно или по glob-паттерну)
4. Fallback на `default`
5. Если не найдено — Claude спросит пользователя
6. Если база не зарегистрирована — Claude предложит `/db-list add`
## Настройки инструментов
### `webPath` — Apache HTTP Server
Путь к каталогу Apache. Используется навыками `/web-publish`, `/web-info`, `/web-stop`, `/web-unpublish`.
Если не задан — ищется в `tools/apache24` от корня проекта. При первом вызове `/web-publish` Apache скачивается автоматически.
Подробнее — в [гайде по веб-публикации](web-guide.md).
### `ffmpegPath` — ffmpeg
Путь к исполняемому файлу ffmpeg. Используется навыком `/web-test` для записи видео.
Если не задан — ищется по порядку:
1. `tools/ffmpeg/bin/ffmpeg.exe` (от корня проекта)
2. `ffmpeg` в системном PATH
Подробнее — в [гайде по записи видео](web-test-recording-guide.md).
### `tts` — озвучка видеоинструкций
| Поле | Тип | По умолчанию | Описание |
|------|-----|-------------|----------|
| `provider` | string | `"edge"` | Провайдер: `"edge"`, `"elevenlabs"`, `"openai"` |
| `voice` | string | `"ru-RU-DmitryNeural"` | Голос (имя или ID в зависимости от провайдера) |
| `apiKey` | string | — | API-ключ (для elevenlabs, openai) |
| `apiUrl` | string | — | URL сервиса (для openai-совместимых) |
| `model` | string | — | Модель (для openai) |
Подробнее о выборе провайдера и голосов — в [гайде по записи видео](web-test-recording-guide.md#доступные-голоса-и-провайдеры).
### `webUrl` — URL веб-клиента (per-database)
URL для открытия базы в браузере через `/web-test`. Задаётся в записи конкретной базы.
Если не задан — `/web-test` берёт URL из активной веб-публикации (`/web-publish`).
Полезно, если веб-клиент доступен по нестандартному адресу (другой порт, внешний сервер, reverse proxy).
## Рекомендуемая структура проекта
Исходники 1С удобно держать под единым каталогом `src/`:
```
src/
cf/ # XML-выгрузка конфигурации (configSrc базы → "src\\cf")
cfe/<ИмяРасширения>/ # исходники расширения (CFE)
epf/<ИмяОбработки>/ # исходники внешней обработки (EPF)
erf/<ИмяОтчёта>/ # исходники внешнего отчёта (ERF)
```
В `.v8-project.json` напрямую участвует только `cf` — через поле `configSrc` базы (`"src\\cf"`).
Каталоги `cfe`/`epf`/`erf` в конфиг не прописываются: их пути передаются соответствующим навыкам
аргументами при сборке/разборке (`epf-build -SourceFile src\\epf\\<Имя>\\<Имя>.xml`,
`cfe-borrow -SrcDir src\\cfe\\<Имя>` и т.п.). Структура — соглашение, а не требование; навыки работают
с любыми путями.
## Движок: 1cv8 или ibcmd
По умолчанию навыки `/db-*`, `/epf-*`, `/erf-*` работают через конфигуратор (`1cv8.exe`; путь к нему
определяется автоматически по каталогу `v8path`) — менять это не нужно.
При желании ту же операцию можно выполнить через автономный сервер `ibcmd`. Для этого навык должен
получить путь к самому файлу `ibcmd.exe` (каталог `bin` всегда трактуется как `1cv8.exe`). Путь
указывают одним из двух способов:
- **разово, в самой задаче** — назвать полный путь к `ibcmd.exe`, например
`C:\\Program Files\\1cv8\\8.3.24.1691\\bin\\ibcmd.exe`;
- **в файле настроек** — прописать в `v8path` не каталог `bin`, а сам файл `...\\bin\\ibcmd.exe` (тогда через
`ibcmd` пойдут все операции).
Через `ibcmd` навыки работают только с файловыми базами — для клиент-серверных `ibcmd` требует
прямых реквизитов СУБД, которые навыки не запрашивают. Список навыков, работающих через `ibcmd`, —
в [руководстве по базам](db-guide.md#движок-1cv8-или-ibcmd).
На Linux и macOS у `ibcmd` есть ещё одно применение: он не требует графической подсистемы, тогда как
конфигуратор в пакетном режиме может упереться в отсутствие X-сервера. Если операции выполняются на
headless-машине, укажите `ibcmd` в настройках один раз:
```json
{
"v8path": "/opt/1cv8/8.3.24.1691/ibcmd"
}
```
Имя файла на этих системах идёт без расширения — навыки распознают его корректно.
## Дополнительные аргументы платформы (`v8args`, `ibcmdargs`)
У платформы есть общие ключи запуска, которых нет среди параметров навыков: `/UseHwLicenses+`,
`/L`, `/ClearCache`, `/DebuggerURL` и другие. Их перечисляют в `v8args` — и каждый навык,
запускающий `1cv8.exe`, добавит их в свою командную строку:
```json
{
"v8path": "C:\\Program Files\\1cv8\\8.3.27.2074\\bin",
"v8args": ["/UseHwLicenses+"]
}
```
Типичный случай — машина с аппаратной лицензией: без `/UseHwLicenses+` пакетные запуски падают
с «Не найдена лицензия». Ключ машинно-специфичный, поэтому и задаётся один раз на проект.
`ibcmdargs` — то же самое для `ibcmd`; его ключи пишутся в форме `--ключ=значение`. Списки не
пересекаются: каждый применяется только к своему движку, «чужой» просто не используется. Разово
те же аргументы можно передать параметрами `-AdditionalV8Arguments` / `-AdditionalIbcmdArguments`
(список — одной строкой через запятую) — они дописываются после проектных. Подробности и
ограничения — в
[руководстве по базам](db-guide.md#дополнительные-аргументы-платформы).
## Минимальный пример
```json
{
"v8path": "C:\\Program Files\\1cv8\\8.3.24.1691\\bin",
"databases": [
{
"id": "dev",
"name": "Разработка",
"type": "file",
"path": "C:\\Bases\\MyApp"
}
]
}
```
## Полный пример
```json
{
"v8path": "C:\\Program Files\\1cv8\\8.3.24.1691\\bin",
"databases": [
{
"id": "dev",
"name": "Разработка",
"type": "file",
"path": "C:\\Bases\\MyApp_Dev",
"user": "Admin",
"password": "",
"aliases": ["dev", "разработка"],
"branches": ["dev", "develop", "feature/*"],
"configSrc": "src\\cf",
"webUrl": "http://localhost:8081/dev"
},
{
"id": "test",
"name": "Тестовая",
"type": "server",
"server": "srv01",
"ref": "MyApp_Test",
"user": "Администратор",
"password": "",
"aliases": ["test", "тест", "тестовая"],
"branches": ["main", "release/*"]
}
],
"default": "dev",
"webPath": "C:\\tools\\apache24",
"ffmpegPath": "C:\\tools\\ffmpeg\\bin\\ffmpeg.exe",
"tts": {
"provider": "edge",
"voice": "ru-RU-DmitryNeural"
}
}
```
## Связанные навыки
- [Базы данных](db-guide.md) — `/db-list`, `/db-create`, `/db-load-xml`, `/db-dump-xml` и другие
- [Веб-публикация](web-guide.md) — `/web-publish`, `/web-info`, `/web-stop`
- [Тестирование в браузере](web-test-guide.md) — `/web-test`
- [Запись видеоинструкций](web-test-recording-guide.md) — запись видео, субтитры, озвучка