Complete name-first 1C adapter saved-state support
This commit is contained in:
@@ -62,8 +62,66 @@ independent metadata properties and must be filtered as derived evidence.
|
||||
|
||||
The `test2` experiment on 2026-07-15 added a minimal calculation register and
|
||||
its required chart of calculation types through Designer, then saved the
|
||||
extension without applying it. The objects remain intentionally `saved_only`
|
||||
for adapter regression checks.
|
||||
extension without applying it. That historical fixture was later removed from
|
||||
the live saved state. The observations below remain decoder evidence, but they
|
||||
must not be interpreted as proof that the current `upo_test` still contains the
|
||||
object.
|
||||
|
||||
The reproducible fixture requirements are declared in
|
||||
`config/1c_metadata_kind_fixtures.json`. Validate the declaration offline:
|
||||
|
||||
```powershell
|
||||
python scripts/check_1c_metadata_kind_fixtures.py
|
||||
```
|
||||
|
||||
Probe the current adapter and test base:
|
||||
|
||||
```powershell
|
||||
python scripts/check_1c_metadata_kind_fixtures.py --live
|
||||
```
|
||||
|
||||
Use `--require-ready` only as a strict regression gate after the fixtures have
|
||||
been loaded with 1C Designer. The checker never creates metadata and never
|
||||
writes platform SQL. `CalculationRegister` and `Sequence` belong in the saved
|
||||
`test2` extension. Legacy `Interface` requires a dedicated compatible test
|
||||
base, supplied explicitly as
|
||||
`--target-base interface_legacy_base=<base_id>`; it must not be simulated in a
|
||||
modern configuration by editing `Config`, `ConfigCAS`, or `DBNames`.
|
||||
|
||||
At the 2026-07-26 verification point, the `upo_test` server required platform
|
||||
`8.5.1.1236`. The exact Designer build is now installed at
|
||||
`C:\Program Files\1cv8\8.5.1.1236\bin\1cv8.exe`; the previous client-version
|
||||
blocker is closed.
|
||||
|
||||
Export the current saved `test2` sources through operating-system integrated
|
||||
authentication only:
|
||||
|
||||
```powershell
|
||||
.\scripts\export_1c_extension_sources.ps1 `
|
||||
-OutputDirectory C:\Temp\upo-test-test2-source
|
||||
```
|
||||
|
||||
The export helper verifies the exact executable version, refuses a non-empty
|
||||
output directory, always passes `/WA+`, and has no user or credential
|
||||
parameters. It only reads and exports the extension; it does not load, save, or
|
||||
apply a configuration.
|
||||
|
||||
The current workstation account is not mapped to an infobase user on
|
||||
`wsr\upo_test`, so the server export stops with
|
||||
`Пользователь ИБ не идентифицирован`. A disposable local file infobase needs a
|
||||
client license and stops with `Не найдена лицензия`. Therefore provisioning is
|
||||
still blocked by execution environment, not by adapter code or platform
|
||||
version. Continue on a licensed workstation under an already authorized
|
||||
operating-system identity; do not add a user name or password to scripts,
|
||||
manifests, or reports, and do not bypass the block with direct SQL.
|
||||
|
||||
An external structural reference is pinned to
|
||||
`asosnoviy/go1cover@c7e3ea3d90396d341cb98285c29973bcc0a82eca`,
|
||||
path `testresourse/cf/designer/src`. It contains examples of
|
||||
`CalculationRegister`, `Sequence`, and legacy `Interface`. The upstream
|
||||
repository is GPL-3.0; its files are reference input only and are not vendored
|
||||
into this project. The pinned source and per-kind paths are declared in the
|
||||
fixture manifest.
|
||||
|
||||
Observed read-only SQL signatures in `ConfigCASSave`:
|
||||
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
# Политика хранилища по слоям конфигурации 1С
|
||||
|
||||
Каждая конфигурация имеет собственную политику: основная конфигурация — слой
|
||||
`base`, каждое расширение — слой `extension:<GUID>`. Подключение основной
|
||||
конфигурации к хранилищу не означает подключения расширения и наоборот.
|
||||
|
||||
Адаптер работает с метаданными 1С только через SQL. Он не считает SQL-таблицы
|
||||
доказательством нативного захвата объекта в хранилище.
|
||||
|
||||
Заявки на захват и явные ручные подтверждения — это служебные данные адаптера,
|
||||
а не данные 1С. Они хранятся в `/data/onec-repository-locks.json` в named
|
||||
volume Docker `adapter-1c-data` и переживают пересоздание контейнера. Это
|
||||
хранилище не используется для вывода о нативном захвате и не требует записи в
|
||||
какую-либо базу 1С.
|
||||
|
||||
## Настройка
|
||||
|
||||
В `development_layers` для каждого фактически используемого слоя задаётся
|
||||
`repository.connection_state`:
|
||||
|
||||
```json
|
||||
{
|
||||
"base": {
|
||||
"repository": {"mode": "manual", "connection_state": "configured"},
|
||||
"support": {"mode": "unknown"}
|
||||
},
|
||||
"extension:86d12be8-086c-11f0-925c-0050568c1b38": {
|
||||
"repository": {"mode": "none", "connection_state": "not_configured"},
|
||||
"support": {"mode": "unknown"}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Допустимые состояния:
|
||||
|
||||
| Состояние | Политика записи |
|
||||
| --- | --- |
|
||||
| `not_configured` | Слой не подключён к хранилищу; захват не требуется. Ограничения поддержки сохраняются. |
|
||||
| `configured` | Подключение известно. В SQL-only режиме нужен запрос и явное подтверждение захвата. |
|
||||
| `unavailable` | Подключение предполагается, но проверить его нельзя; нужен запрос и явное подтверждение захвата. |
|
||||
| `unknown` | Не делать предположений: запись в слой блокируется до настройки. |
|
||||
|
||||
Для ручного подтверждения всегда передаётся точный объект и явный флаг
|
||||
`user_confirmed_locked`. Имя пользователя хранилища берётся из
|
||||
`repository_user` настройки данного слоя. Поэтому при заполненной настройке
|
||||
его не нужно повторять в каждой заявке или подтверждении. Если клиент всё же
|
||||
передаёт `confirmed_repository_user`, оно должно совпадать с настроенным
|
||||
именем. Если имя не настроено, подтверждение попросит его явно.
|
||||
|
||||
## Сохранение факта подключения агентом
|
||||
|
||||
Агент может сохранить подтверждённый факт в служебной конфигурации адаптера
|
||||
через `repository.layer.connection.set`. Метод не меняет SQL-базу 1С и требует
|
||||
явного флага подтверждения.
|
||||
|
||||
Основная конфигурация подключена к хранилищу:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "repository.layer.connection.set",
|
||||
"payload": {
|
||||
"base_id": "neft",
|
||||
"layer_id": "base",
|
||||
"connection_state": "configured",
|
||||
"repository_user": "ivanov",
|
||||
"confirm_repository_connection_change": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Расширение точно не подключено:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "repository.layer.connection.set",
|
||||
"payload": {
|
||||
"base_id": "neft",
|
||||
"extension_guid": "86d12be8-086c-11f0-925c-0050568c1b38",
|
||||
"connection_state": "not_configured",
|
||||
"confirm_repository_connection_change": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
При `not_configured` адаптер устанавливает режим слоя `none`. При
|
||||
`configured` или `unavailable` новый слой по умолчанию получает ручную
|
||||
политику захвата. Рекомендуется сразу передать постоянный
|
||||
`repository_user`: он сохраняется только в конфигурации адаптера для этого
|
||||
слоя и используется во всех последующих ручных подтверждениях.
|
||||
|
||||
## Ручное подтверждение захвата
|
||||
|
||||
После `repository.lock.request` клиент должен использовать возвращённый
|
||||
`next_call` без самостоятельного подбора имён полей. Поля из
|
||||
`next_call.params` передаются как RPC `payload`; его эквивалент:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "repository.lock.confirm",
|
||||
"payload": {
|
||||
"base_id": "neft",
|
||||
"request_id": "rreq-…",
|
||||
"user_confirmed_locked": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`objects`, `operation` и `layer_id` не передаются повторно: подтверждение
|
||||
берёт их из неизменяемой сохранённой заявки. Если имя пользователя сохранено
|
||||
для слоя, оно тоже не передаётся. Успешный ответ содержит `lock_session_id`;
|
||||
его необходимо передать в preflight и операцию записи.
|
||||
|
||||
Ответ `repository.lock.confirm` содержит готовый `write_context`. Его можно
|
||||
передать целиком либо верхнеуровневым полем `write_context`, либо алиасом
|
||||
`repository_lock`; адаптер нормализует поля до проверки захвата:
|
||||
|
||||
```json
|
||||
{
|
||||
"base_id": "neft",
|
||||
"target": {"module_id": "Config:<form-guid>.0"},
|
||||
"repository_lock": {"lock_session_id": "rlock-…", "repository_object": "Document.Имя.Form.ИмяФормы", "layer_id": "base"}
|
||||
}
|
||||
```
|
||||
|
||||
Если одновременно переданы вложенное и верхнеуровневое значение, они должны
|
||||
совпадать; иначе возвращается `repository_lock_context_conflict`.
|
||||
|
||||
## Проверка
|
||||
|
||||
`repository.layers.audit` обнаруживает основную конфигурацию и расширения из
|
||||
live SQL и выдаёт для каждого слоя состояние хранилища, общий режим поддержки
|
||||
и безопасное следующее действие:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "repository.layers.audit",
|
||||
"payload": {"base_id": "neft"}
|
||||
}
|
||||
```
|
||||
|
||||
Ключевые действия в ответе:
|
||||
|
||||
- `repository_lock_not_required` — слой явно не подключён к хранилищу;
|
||||
- `request_and_confirm_repository_capture` — перед записью требуется
|
||||
подтверждение захвата;
|
||||
- `configure_repository_connection_state` — слой нельзя менять, пока не
|
||||
установлен один из статусов подключения.
|
||||
|
||||
Проверка поддержки выполняется отдельно от хранилища. Для конкретного объекта
|
||||
используйте `metadata.write.preflight`: разрешение объекта определяет его
|
||||
`origin`, и адаптер применяет политику найденного слоя, а не клиентский выбор
|
||||
слоя.
|
||||
|
||||
## Захват формы владельца
|
||||
|
||||
Для формы документа, справочника, обработки или отчёта область захвата задаётся
|
||||
точно, без сворачивания к владельцу:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "repository.lock.request",
|
||||
"payload": {
|
||||
"base_id": "neft",
|
||||
"objects": ["Document.тл_Планировщик.Form.ФормаДокументаНовая"],
|
||||
"operation": "modify"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
До создания заявки `repository.lock.plan` и сама заявка разрешают форму через
|
||||
live SQL, получают GUID формы и сохраняют точную каноническую область. После
|
||||
ручного подтверждения эта сессия не даёт права на соседние формы или модуль
|
||||
владельца.
|
||||
|
||||
Также принимается `Form.<GUID>`. Для него адаптер проверяет связь формы с
|
||||
владельцем по live SQL; при отсутствии готовой производной записи владельца
|
||||
выполняется ограниченный SQL-скан форм поддерживаемых типов владельцев. Если
|
||||
владелец не найден, планирование и заявка завершаются `not_found`, а не
|
||||
возвращают ложный `ready`.
|
||||
@@ -126,7 +126,10 @@ REST and MCP selector-chain smoke tests. The selector-chain smoke also checks,
|
||||
when a routine name is available from module metadata, that
|
||||
`metadata.resolve_overrides` returns `write_plan_evidence.next_resolution` for
|
||||
`metadata.saved_state.modules.search` with the same `base_id` and routine query,
|
||||
then follows that resolver. If a saved-state stream exposes `write_plan_target`,
|
||||
plus a public object `ref`/`kind`/`name` and semantic
|
||||
`layer=base_saved_state|extension_saved_state`; public follow-ups never require
|
||||
`ConfigSave`, `ConfigCASSave`, or `owner_guid`. The smoke then follows that
|
||||
resolver. If a saved-state stream exposes `write_plan_target`,
|
||||
the smoke composes it with `write_plan_evidence.target` and checks the resulting
|
||||
read-only `metadata.write.plan`; when the test base has no saved-state stream,
|
||||
that last composition step records `skipped_no_saved_state_target`.
|
||||
@@ -506,6 +509,23 @@ roles/users can read or write this metadata object". Do not depend on
|
||||
`metadata.objects.list kind=Role`: some live bases do not expose roles as regular
|
||||
metadata objects through that route.
|
||||
|
||||
For BSP data-access keys, keep the metadata object and concrete data record
|
||||
separate:
|
||||
|
||||
```json
|
||||
{
|
||||
"base_id": "upo_test",
|
||||
"kind": "object",
|
||||
"object_ref": "Справочники.Номенклатура",
|
||||
"record_ref": "00112233445566778899AABBCCDDEEFF"
|
||||
}
|
||||
```
|
||||
|
||||
Here `kind=object` is the `access_keys_query` area, not the metadata kind.
|
||||
`object_ref` is resolved to the internal BSP SQL number and `record_ref` to the
|
||||
record id. The same public pair works with `access_object_keys_resolve` and
|
||||
`access_object_explain`; do not invent `object_sql_number` in MCP code.
|
||||
|
||||
For BSP role-right checks, `action=write` means add or modify rights. Read-only
|
||||
roles must not be returned for `write`; query `action=read` separately when you
|
||||
need visibility roles. Returned permissions keep `source_fields` from
|
||||
@@ -634,6 +654,26 @@ Agent rules:
|
||||
`metadata.form.command_button.write` for the complete form command + visible
|
||||
button + handler workflow. `code.write` remains a compatibility shortcut for
|
||||
simple module edits.
|
||||
- For a metadata object's standard `synonym` or `comment`, call generic
|
||||
`onec_request` with `method=metadata.object.property.write`, a public
|
||||
`ref`/`kind` + `name`, and `allow_saved_state_write=true`. Prefer
|
||||
`execution_mode=plan` first. The adapter resolves GUIDs and serialized paths;
|
||||
MCP callers must not pass SQL tables or file names. Object rename and adding
|
||||
a new synonym locale are intentionally unsupported.
|
||||
- For an existing requisite, tabular section, dimension, or resource, add a
|
||||
public `member_ref` such as
|
||||
`Catalog.Номенклатура.Attribute.Артикул`. When only
|
||||
`member_kind` + `member_name` is used and several identities match, preserve
|
||||
the adapter's `ambiguous` result and retry with the full member path.
|
||||
- To add a requisite, call `metadata.object.member.add` through `onec_request`
|
||||
with a full `template_member_ref` from the same object and the new public
|
||||
name/synonym. Never invent a GUID, Config table, collection path, or type
|
||||
descriptor in MCP code; the new requisite inherits type/settings from the
|
||||
named template.
|
||||
- The same method adds a tabular-section column when
|
||||
`template_member_ref` contains the full
|
||||
`...TabularSection.<name>.Attribute.<column>` path. Keep the complete path so
|
||||
duplicate checks and generated identity remain scoped to that table part.
|
||||
- For unresolved module owners, inspect `diagnostics.owner_resolution` and
|
||||
`counts.owner_scan_limit_hit`; narrow by `kind`/`name`/`guid` or increase
|
||||
`owner_scan_limit` before falling back to broader searches.
|
||||
|
||||
Reference in New Issue
Block a user