Initial project import
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# 1C Agent Coding Contract
|
||||
|
||||
This contract is the default rule set for coding agents that work through the
|
||||
1C adapter.
|
||||
1C adapter. The agent calls it only through the `onec_request` MCP tool; the
|
||||
REST SQL adapter is MCP's private downstream transport.
|
||||
|
||||
## Default View
|
||||
|
||||
@@ -13,6 +14,57 @@ This contract is the default rule set for coding agents that work through the
|
||||
- Objects can exist only in saved-state and can later be activated or canceled.
|
||||
Do not hide them from the agent view.
|
||||
|
||||
### Empty saved-state layer
|
||||
|
||||
An empty saved-state layer is normal before the first edit. In that case an
|
||||
agent still uses only public selectors and reports the adapter result; it must
|
||||
not obtain an active `ConfigCAS`/`Config` module reference and retry by hand.
|
||||
|
||||
Known adapter limitation (2026-08-02): for an extension-owned object module
|
||||
with no prepared saved-state row, public `code.read` can return
|
||||
`source_missing` even though the active module is proven to exist. Treat this
|
||||
as an adapter defect, not as evidence that the 1C object or BSL is absent.
|
||||
Use a developer-owned diagnostic check to investigate it; do not expose its
|
||||
storage coordinates to a coding agent.
|
||||
|
||||
## Required adapter acceptance fixture
|
||||
|
||||
`upo_test` must contain one isolated extension-owned `Report` with a decoded
|
||||
`object_module`, a unique BSL anchor, and no shared business role. This fixture
|
||||
is created and maintained by a human in Configurator; the SQL-only adapter must
|
||||
not fabricate it. It is the required target for the public acceptance sequence:
|
||||
|
||||
```text
|
||||
code.read(ref, extension, module_ordinal=1)
|
||||
→ code.write(old, new, apply_and_rollback)
|
||||
→ code.search verifies new text
|
||||
→ rollback and saved-state cleanup
|
||||
```
|
||||
|
||||
The request must contain no `layer`, table, file name, module ref, payload hash,
|
||||
or stream index. Until the fixture exists, unit tests prove routing only; they
|
||||
do not prove a live extension Report write.
|
||||
|
||||
### Verified base-module smoke
|
||||
|
||||
On 2026-08-02 the public sequence was verified on `upo_test` against base
|
||||
`Report.АвтоматическиеСкидки`, `module_ordinal=1`: a unique comment replacement
|
||||
completed in about 25 seconds with `verified_and_rolled_back`. The adapter
|
||||
auto-prepared `ConfigSave`, read back the write, rolled back the BSL change,
|
||||
then removed the exact rows it had prepared. The public result contains the
|
||||
opaque `prepare_receipt_id` and `prepare_cleanup`; the final saved-state status
|
||||
was `empty`. This validates the base route only, not the extension Report route.
|
||||
Live check on 2026-08-02 found 19 extensions but zero extension-owned Reports
|
||||
in `upo_test`; therefore the required extension acceptance fixture is currently
|
||||
missing and the extension write branch remains unaccepted.
|
||||
|
||||
Live check on 2026-08-13 found an extension BSL stream for
|
||||
`Report.УОП_ИнвентаризационнаяОпись`, but its `role_status=unconfirmed`.
|
||||
That is read-only evidence, not an acceptance fixture: do not write through an
|
||||
ordinal, storage reference, or guessed Configurator role. A human must add or
|
||||
identify one extension Report with a decoded `object_module` before the live
|
||||
extension `apply_and_rollback` test can run.
|
||||
|
||||
## Compare Views
|
||||
|
||||
- Use `state=both` or `source_state=all` only when the task needs a comparison
|
||||
@@ -33,6 +85,34 @@ Use public names and selectors:
|
||||
3. `code.read` with `state=working` to read the module or routine.
|
||||
4. `code.read` with `state=both` only for an explicit saved-vs-active check.
|
||||
|
||||
For a report form, keep the descriptions separate:
|
||||
|
||||
1. `metadata.object.forms(ref=Report.<name>, source_state=working)` lists the
|
||||
report's form references.
|
||||
2. `metadata.object.form.details` with that same public report ref and the
|
||||
returned form name reads the form description (attributes, parameters,
|
||||
commands, items, and the form module).
|
||||
3. `metadata.form.decode(view=structure)` is an optional compact static
|
||||
projection. It must retain `unresolved` parent/child links where no SQL
|
||||
codec has proved them.
|
||||
|
||||
A form command does not have its own module. Its handler is a named routine in
|
||||
the form module only when the decoder returns an evidenced command link.
|
||||
|
||||
Use the `read_selector.selector_token` returned by discovery with the method
|
||||
declared in that selector. Do not copy GUIDs, module refs, table names, or file
|
||||
names into a follow-up request. A module ordinal, display name, or storage-derived role
|
||||
is not a Configurator-tree path. If a result has `role_status=unconfirmed` or
|
||||
`configurator_path_status=unconfirmed`, the agent must not name, write, or
|
||||
infer its tree owner; report the missing decoder evidence instead.
|
||||
|
||||
When checking object commands, call `metadata.object.commands` with the same
|
||||
public `ref`/`kind`+`name` and `extension` selector. The adapter resolves the
|
||||
extension name and reads its active metadata itself. A prior `not_found` from a
|
||||
route that did not carry the extension context is not evidence that the report
|
||||
has no commands. Conversely, a BSL stream suffix alone is never evidence of a
|
||||
command or of its module path.
|
||||
|
||||
Agents should ask for and report object names, routine names, and code text.
|
||||
They should not ask users for SQL tables, storage file names, stream indexes, or
|
||||
saved-state write flags during normal coding work.
|
||||
@@ -52,12 +132,52 @@ Supported public edit shapes:
|
||||
say "save this code" and send the desired code text. It must not ask whether SQL
|
||||
saved-state apply flags are allowed.
|
||||
|
||||
For extension object modules, preparation maps the active payload route to its
|
||||
canonical saved-state filename internally. A public `code.write` therefore
|
||||
continues with `extension`, `ref`, module role, and the proven replacement
|
||||
only; it must never ask the caller to supply the canonical filename or stream.
|
||||
|
||||
Every successful `code.write` response must show:
|
||||
|
||||
- `write_mode.target=saved_state`;
|
||||
- `write_mode.activation_state=not_activated`;
|
||||
- `write_mode.production_apply=false`.
|
||||
|
||||
### Repository-controlled extension writes
|
||||
|
||||
For a repository-controlled extension, use the public sequence below and stop
|
||||
when it asks for a human Configurator action:
|
||||
|
||||
```text
|
||||
code.search(extension, ref, old fragment)
|
||||
→ repository.lock.plan(extension, ref)
|
||||
→ repository.lock.request
|
||||
→ human captures the exact object in Configurator
|
||||
→ repository.lock.confirm
|
||||
→ code.write(extension, ref, module_ordinal, old, new, repository_lock)
|
||||
→ code.search readback
|
||||
```
|
||||
|
||||
The lock plan for `upo / фс_Отчеты1 /
|
||||
Report.фс_ПродовольственнаяКорзина` resolves exactly one object:
|
||||
`Отчет.фс_ПродовольственнаяКорзина`, in extension layer
|
||||
`extension:8e02accd-8a34-11f1-8294-005056b0d483`. SQL cannot prove a native
|
||||
Configurator capture; only a human confirmation can continue this route.
|
||||
|
||||
`metadata.write.preflight` currently plans canonical high-level
|
||||
`metadata.write` targets and may return `needs_route` for this compatibility
|
||||
`code.write` shape before saved-state preparation. This is not permission to
|
||||
use its low-level payload hint. Use `code.write(mode=plan)` instead: a first
|
||||
extension edit may return public `needs_prepare`, which means to confirm the
|
||||
repository lock and reissue the same public request in an apply mode. Keep
|
||||
using the public `code.write` route; report any different route failure to
|
||||
adapter developers.
|
||||
|
||||
If it returns `extension_saved_state_prepare_protocol_unproven`, do not retry
|
||||
or supply technical coordinates. The adapter has not yet proven the exact
|
||||
saved-state preparation codec for this extension layout; report it for adapter
|
||||
development. Do not infer that the extension was not saved in Configurator.
|
||||
|
||||
## Hidden Storage Details
|
||||
|
||||
The form module container marker `///----` is adapter-owned storage syntax.
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
# Актуальный векторный поиск по коду 1С
|
||||
|
||||
Цель: семантический поиск по локальному SQL-индексу адаптера без потери
|
||||
актуальности кода. Вектор является только производным кешем: перед выдачей
|
||||
адаптер сверяет найденные фрагменты с текущим состоянием конфигурации и
|
||||
отбрасывает либо переиндексирует устаревшие записи.
|
||||
|
||||
## Выбранная модель
|
||||
|
||||
- `Qwen/Qwen3-Embedding-0.6B-GGUF`, квантование `Q8_0`;
|
||||
- OpenAI-compatible endpoint на `http://docker-gpu.cin.su:8082`;
|
||||
- `llama.cpp`, `--embedding --pooling last`;
|
||||
- CPU-only (`--n-gpu-layers 0`), чтобы не менять работающие GPU-сервисы;
|
||||
- endpoint возвращает 1024 измерения, клиент использует Matryoshka-срез до
|
||||
запрошенных 384 измерений и повторно нормализует его.
|
||||
|
||||
Образ `llama.cpp` зафиксирован digest, а модель — официальным repository/quant
|
||||
селектором. Это исключает незаметную смену runtime при повторном deploy.
|
||||
|
||||
Модель и образ публичные, endpoint работает в изолированном тестовом контуре
|
||||
без токена. Постоянный кеш модели хранится вне git в
|
||||
`Z:\LLM\models\cache\llama.cpp`.
|
||||
|
||||
## Развёртывание
|
||||
|
||||
Проверить итоговую конфигурацию:
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/deploy_embeddings.ps1 -ConfigOnly
|
||||
```
|
||||
|
||||
Запустить сервис с загрузкой образа:
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/deploy_embeddings.ps1 -Pull
|
||||
```
|
||||
|
||||
Первый запуск скачивает модель в постоянный кеш и поэтому может занять
|
||||
несколько минут. Скрипт ждёт `/health`, проверяет имя модели и делает реальный
|
||||
запрос к `/v1/embeddings`.
|
||||
|
||||
## Обновление векторов
|
||||
|
||||
Сначала адаптер должен содержать актуальные текстовые chunks. Затем worker
|
||||
забирает только отсутствующие либо изменившиеся фрагменты:
|
||||
|
||||
```powershell
|
||||
python scripts/embed_1c_code_vectors.py `
|
||||
--adapter-url http://docker.cin.su:8011/rpc `
|
||||
--base-id upo_test `
|
||||
--embedding-provider openai-compatible `
|
||||
--embedding-model qwen3-embedding-0.6b `
|
||||
--dimensions 384 `
|
||||
--embedding-base-url http://docker-gpu.cin.su:8082 `
|
||||
--limit 500 `
|
||||
--batch-size 8 `
|
||||
--chunk-kind routine `
|
||||
--max-text-chars 4000 `
|
||||
--json
|
||||
```
|
||||
|
||||
Worker по умолчанию индексирует `routine`: процедуры и функции дают наиболее
|
||||
точный контекст для программирования и заметно быстрее пересчитываются при
|
||||
частых изменениях. Для диагностического покрытия модульных фрагментов можно
|
||||
добавить второй `--chunk-kind module`; это более дорогой отдельный проход.
|
||||
`--max-text-chars` не обрезает код молча: длинные chunks пропускаются в этом
|
||||
проходе и остаются pending. Их нужно разбивать на окна отдельной задачей либо
|
||||
индексировать в период низкой нагрузки с большим лимитом.
|
||||
|
||||
Размерность входит в имя кеша (`openai-compatible:qwen3-embedding-0.6b@d384`),
|
||||
поэтому векторы разных размеров никогда не смешиваются.
|
||||
|
||||
## Поиск
|
||||
|
||||
```powershell
|
||||
python scripts/search_1c_code_vectors.py `
|
||||
"где рассчитывается сумма документа перед проведением" `
|
||||
--adapter-url http://docker.cin.su:8011/rpc `
|
||||
--base-id upo_test `
|
||||
--embedding-provider openai-compatible `
|
||||
--embedding-model qwen3-embedding-0.6b `
|
||||
--dimensions 384 `
|
||||
--embedding-base-url http://docker-gpu.cin.su:8082 `
|
||||
--limit 10 `
|
||||
--json
|
||||
```
|
||||
|
||||
Поиск вызывается с `strict=true` и `verify=true`. Сохранённые изменения
|
||||
Конфигуратора перекрывают активную конфигурацию, а удалённые/изменённые chunks
|
||||
не возвращаются по старому вектору.
|
||||
|
||||
Для активного кода расширений хеш `ConfigCAS` разрешается через текущий
|
||||
manifest расширения в descriptor объекта. Результат содержит обычные
|
||||
`kind/name`, а также `extension` и `extension_guid`; вызывающему коду не нужно
|
||||
работать с CAS-хешами как с именами объектов.
|
||||
|
||||
Перед первым глобальным поиском после обновления адаптера нужно постранично
|
||||
заполнить локальную карту владельцев:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "metadata.module_owner_cache.backfill",
|
||||
"payload": {
|
||||
"base_id": "upo_test",
|
||||
"limit": 50,
|
||||
"kind_index": 0,
|
||||
"offset": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Следующий вызов получает `kind_index` и `offset` из `next_cursor`. Повторять до
|
||||
`complete=true`. Операция читает актуальные метаданные, но пишет только в
|
||||
локальный SQLite адаптера. Найденные имена сразу добавляются в существующие
|
||||
строки лексического и векторного индексов; перестраивать embeddings не нужно.
|
||||
По умолчанию объекты без строк code index быстро пропускаются. `deep=true`
|
||||
нужен только для отдельного фонового заполнения владельцев неиндексированных
|
||||
модулей и не должен использоваться в интерактивном поиске.
|
||||
|
||||
Для `Qwen3-Embedding` клиент автоматически добавляет к векторизуемому запросу
|
||||
англоязычную инструкцию поиска по исходному коду 1С, как рекомендует карточка
|
||||
модели. В `query` адаптера остаётся исходный русский текст, поэтому лексическая
|
||||
часть гибридного поиска не загрязняется служебным префиксом.
|
||||
|
||||
## Отдельная векторная БД
|
||||
|
||||
На текущем этапе не требуется. Векторы хранятся рядом с индексом адаптера в
|
||||
SQLite и выбираются линейным сканированием. Это проще и гарантирует атомарную
|
||||
проверку актуальности. Отдельный ANN-движок имеет смысл только после замера
|
||||
десятков тысяч актуальных chunks и неприемлемой задержки; источником истины всё
|
||||
равно остаётся 1С/SQL, а ANN должен хранить `chunk_id` и `text_sha1` как
|
||||
проверяемую производную копию.
|
||||
@@ -6,7 +6,7 @@ form decoder reached zero missing items and zero mismatches.
|
||||
## Target
|
||||
|
||||
- Base: `upo_test`
|
||||
- Adapter: `http://docker-gpu.cin.su:8011`
|
||||
- Adapter: `http://docker.cin.su:8011`
|
||||
- Saved-state table: `ConfigCASSave`
|
||||
- Form payload file:
|
||||
`f96a0c45-3eff-11f1-8287-005056b0d483__25c39fbf-35a4-4b43-8e3f-cd1f91082c88.0`
|
||||
@@ -104,7 +104,7 @@ payload = {
|
||||
"timeout_seconds": 60,
|
||||
"max_items": 5000
|
||||
}
|
||||
req = urllib.request.Request("http://docker-gpu.cin.su:8011/rpc", data=json.dumps({"method":"metadata.write_learning.capture_after","payload":payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
req = urllib.request.Request("http://docker.cin.su:8011/rpc", data=json.dumps({"method":"metadata.write_learning.capture_after","payload":payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
print(urllib.request.urlopen(req, timeout=90).read().decode("utf-8"))
|
||||
'@ | python -
|
||||
```
|
||||
@@ -116,7 +116,7 @@ Then run:
|
||||
import json, urllib.request
|
||||
for method in ("metadata.write_learning.diff", "metadata.write_learning.infer_rule"):
|
||||
payload = {"learning_id": "form-command-binding-standard-customize-form"}
|
||||
req = urllib.request.Request("http://docker-gpu.cin.su:8011/rpc", data=json.dumps({"method": method, "payload": payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
req = urllib.request.Request("http://docker.cin.su:8011/rpc", data=json.dumps({"method": method, "payload": payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
print(urllib.request.urlopen(req, timeout=90).read().decode("utf-8"))
|
||||
'@ | python -
|
||||
```
|
||||
@@ -151,7 +151,7 @@ payload = {
|
||||
"timeout_seconds": 60,
|
||||
"max_items": 5000
|
||||
}
|
||||
req = urllib.request.Request("http://docker-gpu.cin.su:8011/rpc", data=json.dumps({"method":"metadata.write_learning.capture_after","payload":payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
req = urllib.request.Request("http://docker.cin.su:8011/rpc", data=json.dumps({"method":"metadata.write_learning.capture_after","payload":payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
print(urllib.request.urlopen(req, timeout=90).read().decode("utf-8"))
|
||||
'@ | python -
|
||||
```
|
||||
@@ -163,7 +163,7 @@ Then run:
|
||||
import json, urllib.request
|
||||
for method in ("metadata.write_learning.diff", "metadata.write_learning.infer_rule"):
|
||||
payload = {"learning_id": "form-command-binding-local-apply-command"}
|
||||
req = urllib.request.Request("http://docker-gpu.cin.su:8011/rpc", data=json.dumps({"method": method, "payload": payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
req = urllib.request.Request("http://docker.cin.su:8011/rpc", data=json.dumps({"method": method, "payload": payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
print(urllib.request.urlopen(req, timeout=90).read().decode("utf-8"))
|
||||
'@ | python -
|
||||
```
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# 1C Form Discovery And Editing
|
||||
|
||||
The semantic object/form boundary for agent responses is defined in
|
||||
[`1c-metadata-structure.md`](1c-metadata-structure.md). In particular, an
|
||||
owner returns references to forms; form parameters, attributes, commands,
|
||||
elements, and form-module facts are read through the form's own public
|
||||
selector. Do not duplicate a decoded form as an invented subtree of its owner.
|
||||
|
||||
This runbook adapts the MOXCEL discovery loop to managed forms. The goal is a
|
||||
full SQL-side form decoder and safe saved-state editing through the test
|
||||
extension, with XML exports used only as evidence fixtures.
|
||||
@@ -7,7 +13,7 @@ extension, with XML exports used only as evidence fixtures.
|
||||
## Current Baseline
|
||||
|
||||
- Default base: `upo_test`.
|
||||
- Default adapter endpoint: `http://docker-gpu.cin.su:8011`.
|
||||
- Default adapter endpoint: `http://docker.cin.su:8011`.
|
||||
- Primary test extension/object fixture:
|
||||
`фс_ДоработкиОбщее` /
|
||||
`DataProcessor.фс_НастройкаУсловногоОформления`.
|
||||
@@ -142,7 +148,7 @@ Decode a concrete saved-state form payload:
|
||||
|
||||
```powershell
|
||||
python scripts/smoke_1c_write_matrix.py `
|
||||
--base-url http://docker-gpu.cin.su:8011 `
|
||||
--base-url http://docker.cin.su:8011 `
|
||||
--base-id upo_test `
|
||||
--table ConfigCASSave `
|
||||
--file-name <form-file-name> `
|
||||
@@ -177,7 +183,7 @@ Build a decoder coverage and gap profile for an object form:
|
||||
|
||||
```powershell
|
||||
python scripts/profile_1c_forms.py `
|
||||
--adapter-url http://docker-gpu.cin.su:8011 `
|
||||
--adapter-url http://docker.cin.su:8011 `
|
||||
--base-id upo_test `
|
||||
--kind Catalog `
|
||||
--name ЗадачиАссистентаУправления `
|
||||
@@ -223,7 +229,7 @@ Run the existing source-aware route smoke:
|
||||
|
||||
```powershell
|
||||
python scripts/smoke_1c_saved_state_write_routes.py `
|
||||
--base-url http://docker-gpu.cin.su:8011 `
|
||||
--base-url http://docker.cin.su:8011 `
|
||||
--base-id upo_test `
|
||||
--table ConfigCASSave `
|
||||
--file-name <form-file-name> `
|
||||
@@ -234,7 +240,7 @@ Then run the matrix smoke:
|
||||
|
||||
```powershell
|
||||
python scripts/smoke_1c_write_matrix.py `
|
||||
--base-url http://docker-gpu.cin.su:8011 `
|
||||
--base-url http://docker.cin.su:8011 `
|
||||
--base-id upo_test `
|
||||
--table ConfigCASSave `
|
||||
--file-name <form-file-name> `
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
# Структура метаданных конфигурации 1С
|
||||
|
||||
Этот документ задаёт модель, которой должны следовать агенты и публичные
|
||||
ответы SQL-адаптера. Он описывает семантические метаданные, а не физические
|
||||
имена SQL-записей, пути контейнеров или номера потоков.
|
||||
|
||||
## Главное правило
|
||||
|
||||
У конфигурации нет единого шаблона дочерних узлов для всех классов объектов.
|
||||
Набор узлов определяется классом объекта и подтверждается двумя источниками:
|
||||
|
||||
1. документацией платформы 1С для соответствующего класса;
|
||||
2. живым чтением выбранного объекта через адаптер.
|
||||
|
||||
Не добавляйте в ответ ни табличную часть, ни форму, ни модуль только потому,
|
||||
что они есть у другого объекта. Если живой декодер не подтвердил конкретный
|
||||
дочерний объект, верните явный неполный результат, а не пустой вымышленный
|
||||
узел.
|
||||
|
||||
## Связь объекта и формы
|
||||
|
||||
Форма — самостоятельный объект метаданных, на который владелец ссылается в
|
||||
своей коллекции `Формы`. В кратком описании владельца возвращается только
|
||||
ссылка/идентичность формы: имя, GUID и доступный публичный селектор. Полное
|
||||
описание формы читается отдельным запросом.
|
||||
|
||||
```text
|
||||
Отчет
|
||||
└─ Формы
|
||||
└─ <ссылка на Форму>
|
||||
|
||||
Форма
|
||||
├─ Реквизиты
|
||||
├─ Параметры
|
||||
├─ Команды
|
||||
├─ Модуль формы
|
||||
└─ Элементы
|
||||
```
|
||||
|
||||
Параметры формы не являются её реквизитами. Это декларация параметризации при
|
||||
открытии формы. Команда формы не имеет отдельного модуля: её имя обработчика
|
||||
является свойством команды и разрешается в модуле формы. Аналогично имя
|
||||
обработчика события элемента — свойство элемента, а не дочерний узел дерева.
|
||||
|
||||
Подчинённая команда владельца (`Отчет.Команды.<Имя>`) отличается от команды
|
||||
формы. Если документация и живая база подтверждают модуль команды, его надо
|
||||
сообщать как свойство этой команды, не выдавая за форму или за модуль формы.
|
||||
|
||||
## Отчёт
|
||||
|
||||
Официальная методическая документация 1С подтверждает, что отчёт может иметь
|
||||
реквизиты и табличные части. Табличная часть, в свою очередь, имеет реквизиты.
|
||||
Для отчёта допустима следующая *классовая* схема; конкретные экземпляры
|
||||
показываются только после живого чтения:
|
||||
|
||||
```text
|
||||
Отчет
|
||||
├─ Реквизиты
|
||||
├─ Табличные части
|
||||
│ └─ Реквизиты табличной части
|
||||
├─ Формы → отдельные описания Форм
|
||||
├─ Команды
|
||||
├─ Макеты
|
||||
├─ Модуль объекта
|
||||
└─ Модуль менеджера
|
||||
```
|
||||
|
||||
СКД — это тип/содержимое макета, а не обязательная отдельная ветвь любого
|
||||
отчёта. Не создавайте узел СКД, если в живом объекте не подтверждён
|
||||
соответствующий макет.
|
||||
|
||||
## Публичные маршруты адаптера
|
||||
|
||||
Для объекта `Отчет.<Имя>` адаптер использует следующие раздельные операции:
|
||||
|
||||
1. `metadata.object.forms` — читает подтверждённые ссылки отчёта на формы;
|
||||
возвращаемые строки содержат идентичность формы, а не её элементы,
|
||||
реквизиты, параметры или текст модуля.
|
||||
2. `metadata.object.form.details` — по владельцу и имени формы получает
|
||||
развёрнутое описание формы.
|
||||
3. `metadata.form.decode` — декодирует одну конкретную форму и возвращает её
|
||||
параметры, реквизиты, элементы, команды, события и сводку встроенного
|
||||
модуля.
|
||||
|
||||
Передавайте `configuration_view=effective_working` либо не передавайте view:
|
||||
MCP установит этот режим сам. Он означает логическую рабочую конфигурацию с
|
||||
учётом базовой конфигурации, сохранённых изменений и расширений; он не
|
||||
разрешает агенту выбирать `ConfigSave` или `ConfigCASSave`.
|
||||
|
||||
Таким образом, ссылка `Отчет → Форма` не должна заменяться копией описания
|
||||
формы внутри объекта отчёта. Агрегированный `metadata.object.full` удобен для
|
||||
обзора, но для работы с формой агент обязан сохранять её отдельный публичный
|
||||
селектор и при необходимости вызвать один из двух form-методов выше.
|
||||
|
||||
Текущая реализация подтверждает маршрут для `Report`: в
|
||||
`RELATED_SECTION_RULES` форма имеет категорию `Form`, а
|
||||
`metadata.object.forms` возвращает только `guid`, `name`, `synonyms` и счётчики
|
||||
частей. Детали добавляет только `metadata.object.form.details` через отдельный
|
||||
вызов `metadata.form.decode`.
|
||||
|
||||
## Источники
|
||||
|
||||
- [Иерархия объектов конфигурации и подчинённые объекты — 1С:EDT](https://its.1c.ru/db/content/edtdoc/src/topics/i030.html)
|
||||
- [Отчёты, реквизиты и табличные части — методическая поддержка 1С](https://its.1c.ru/db/content/metod8dev/src/platform81/startersdev/i8102579.htm)
|
||||
- [Формы: реквизиты, параметры, команды и модуль — руководство разработчика 1С](https://its.1c.ru/db/v8310doc/bookmark/dev/TI000000391)
|
||||
- [Явное объявление параметров формы — стандарт 1С](https://its.1c.ru/db/content/v8std/src/1%C2%A0200/700/i8100741.htm)
|
||||
|
||||
## Проверка перед изменением
|
||||
|
||||
Перед тем как менять форму или модуль, агент обязан выполнить публичное
|
||||
чтение владельца, затем получить форму её собственным селектором. Отсутствие
|
||||
SQL-подключения, нераспознанный контейнер или неоднозначная связь — это
|
||||
`unresolved`/`protocol_incomplete`, а не разрешение достроить структуру по
|
||||
документации.
|
||||
|
||||
`metadata.form.decode` с `view=structure` возвращает компактные доказанные
|
||||
факты по статическим элементам. Пока SQL-кодек логического родителя не
|
||||
доказан, поля `parent` и `children` имеют статус `unresolved`; нельзя строить
|
||||
их из технической глубины или пути сериализованного контейнера.
|
||||
|
||||
Запросы `runtime.form.elements.inspect` и `runtime.form.inspect` возвращают
|
||||
`runtime_inspection_unsupported`. SQL-адаптер не открывает формы, не запускает
|
||||
обработчики и не выдаёт созданные СКД во время работы элементы за статические
|
||||
метаданные.
|
||||
@@ -240,7 +240,7 @@ changes:
|
||||
|
||||
```powershell
|
||||
python scripts\profile_1c_tabular_templates.py `
|
||||
--adapter-url http://docker-gpu.cin.su:8011 `
|
||||
--adapter-url http://docker.cin.su:8011 `
|
||||
--base-id upo_test `
|
||||
--inventory-json reports\1c-template-baselines\upo_test_configuration_tabular_templates.json `
|
||||
--output-json reports\1c-template-baselines\upo_test_tabular_template_profiles.json `
|
||||
|
||||
@@ -116,8 +116,11 @@ SQL удобен как быстрый источник данных, но не
|
||||
canonical path до процедуры, областью является эта процедура/функция; иначе
|
||||
весь текущий saved-модуль.
|
||||
|
||||
По умолчанию `code.write` делает `mode=apply`, но это apply в saved-state
|
||||
слой (`ConfigSave`/`ConfigCASSave`), а не применение конфигурации в runtime.
|
||||
По умолчанию `code.write` делает безопасный `mode=plan` и не пишет в SQL.
|
||||
Только явно переданный `mode=apply`, `apply_and_verify` или
|
||||
`apply_and_rollback` может записать saved-state слой
|
||||
(`ConfigSave`/`ConfigCASSave`); это всё равно не применение конфигурации в
|
||||
runtime.
|
||||
Адаптер сам выставляет save-first gates и сам выбирает физический маршрут.
|
||||
Физические детали возвращаются только при `include_storage=true` для
|
||||
диагностики. Ответ `code.write` всегда содержит `write_mode`: target
|
||||
@@ -134,6 +137,62 @@ source `saved_state`, activation_state `not_activated`.
|
||||
добирается отдельным проходом, а `counts.saved_matches` и
|
||||
`counts.active_matches` показывают покрытие по слоям.
|
||||
|
||||
### Repository lock and first extension edit
|
||||
|
||||
Для изменения объекта в расширении, подключенном к хранилищу, агент сначала
|
||||
использует только публичные вызовы:
|
||||
|
||||
```text
|
||||
code.search → repository.lock.plan → repository.lock.request
|
||||
→ человек захватывает объект в Конфигураторе → repository.lock.confirm
|
||||
→ code.write → code.search (readback)
|
||||
```
|
||||
|
||||
Если у extension-модуля ещё нет saved-state строки, `code.write(mode=plan)`
|
||||
возвращает `needs_prepare` и
|
||||
`diagnostics.next_action=confirm_repository_lock_then_apply`. Это нормальный
|
||||
первый-edit маршрут: после подтверждённого lock тот же публичный `code.write`
|
||||
в apply-режиме сам подготовит saved-state. Агент не передаёт `module_ref`,
|
||||
`stream_index`, таблицу или имя технического файла.
|
||||
|
||||
Исключение: `extension_saved_state_prepare_protocol_unproven` означает, что
|
||||
автоматическая подготовка запрещена. Это не доказательство того, что
|
||||
расширение не сохраняли: адаптер ещё не доказал точный prepare-кодек для
|
||||
наблюдаемой extension-layout. Агент не создаёт контейнер через SQL и передаёт
|
||||
случай разработчикам адаптера без технических координат.
|
||||
|
||||
Acceptance extension write считается пройденным только при наличии в
|
||||
`upo_test` отдельного extension-owned `Report` с object module и успешном
|
||||
публичном `code.write(..., apply_and_rollback)` без storage-координат. Успех
|
||||
base-модуля в `ConfigSave` не доказывает ветку `ConfigCAS → ConfigCASSave`.
|
||||
|
||||
`upo` не используется для автоматических проверочных записей. Контролируемые
|
||||
`apply_and_rollback` проверки разрешены только в `upo_test`.
|
||||
|
||||
Для регрессии первого extension-edit используйте публичный smoke (без SQL
|
||||
таблиц, key, module_ref или GUID в запросе):
|
||||
|
||||
```powershell
|
||||
python scripts\smoke_1c_extension_saved_state_prepare.py --apply
|
||||
```
|
||||
|
||||
Он проверяет `plan → prepare/readback → rollback → immediate plan` на
|
||||
`upo_test / фс_ДоработкиОбщее / Catalog.Номенклатура`. После rollback не
|
||||
должно остаться saved-state строк, а повторный план должен быть `plan_ready`.
|
||||
|
||||
### Safe adapter deployment
|
||||
|
||||
Перед Docker-обновлением скрипт развёртывания запрашивает `/health` и ждёт
|
||||
`runtime.state=ready` и `runtime.active_rpc_count=0`. При остановке REST
|
||||
переходит в `draining`; уже начатые запросы продолжают выполняться до пяти
|
||||
минут. Не используйте `-SkipDrainCheck`, кроме аварийного случая, когда
|
||||
ответственный подтвердил отсутствие активной записи.
|
||||
|
||||
После обновления проверяются REST `/health?base_id=upo_test` и MCP `/health`.
|
||||
JSONL-аудит REST хранится в `/data/adapter-audit.jsonl`, MCP — в
|
||||
`/data/mcp-audit.jsonl`; оба периодически сворачиваются в
|
||||
`/data/adapter-audit-reports/latest.json` на соответствующем хосте.
|
||||
|
||||
Если фрагмент повторяется, агент должен передать более узкий контекст
|
||||
(`routine_name`) или заменить процедуру целиком. Адаптер в такой ситуации
|
||||
возвращает `ambiguous_fragment`, `scope` и `counts.occurrences`, и не
|
||||
|
||||
@@ -52,7 +52,7 @@ MCP server configuration.
|
||||
The MCP proxy does not hard-code the 1C adapter address. Pass it with:
|
||||
|
||||
```text
|
||||
ONEC_ADAPTER_URL=http://docker-gpu.cin.su:8011
|
||||
ONEC_ADAPTER_URL=http://docker.cin.su:8011
|
||||
```
|
||||
|
||||
Optional adapter bearer token:
|
||||
@@ -75,6 +75,37 @@ core/deploy/docker/adapter-1c-mcp/.env.example
|
||||
|
||||
For real deployment, create a non-committed `.env` next to the compose file and set the actual adapter URL/token there.
|
||||
|
||||
## REST adapter on docker.cin.su
|
||||
|
||||
The REST SQL adapter is deployed from:
|
||||
|
||||
```text
|
||||
core/deploy/docker/adapter-1c/compose.yaml
|
||||
```
|
||||
|
||||
Its published port is set in a non-committed REST environment file:
|
||||
|
||||
```text
|
||||
ADAPTER_1C_HOST_PORT=8011
|
||||
```
|
||||
|
||||
SQL-base administration is available in the REST adapter at:
|
||||
|
||||
```text
|
||||
http://docker.cin.su:<ADAPTER_1C_HOST_PORT>/admin
|
||||
```
|
||||
|
||||
With the default port this is `http://docker.cin.su:8011/admin`. The page is
|
||||
used to add or update SQL connections for named bases. Do not put connection
|
||||
passwords in Git or documentation. If the port changes, use the new port both
|
||||
for this page and in `ONEC_ADAPTER_URL`.
|
||||
|
||||
If that port is occupied, select a free port there and set the MCP URL to the
|
||||
same value, for example `ONEC_ADAPTER_URL=http://docker.cin.su:18011`.
|
||||
Keep `ONEC_SQL_BASES_JSON` (or the mounted `/data/onec-sql-bases.json`) only
|
||||
in that external runtime configuration; never place SQL passwords in this
|
||||
repository.
|
||||
|
||||
## Deploy
|
||||
|
||||
Deploy both REST adapter and MCP proxy, then run live verification when a test
|
||||
@@ -83,6 +114,8 @@ base is available:
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass `
|
||||
-File scripts\deploy_1c_adapter_stack.ps1 `
|
||||
-RestEnvFile <path-to-non-committed-rest-env> `
|
||||
-McpEnvFile <path-to-non-committed-mcp-env> `
|
||||
-BaseId <base-id-from-project-context>
|
||||
```
|
||||
|
||||
@@ -692,7 +725,7 @@ Saved-state BSL write smoke:
|
||||
|
||||
```powershell
|
||||
python scripts\smoke_1c_code_write_saved_state.py `
|
||||
--adapter-url http://docker-gpu.cin.su:8011 `
|
||||
--adapter-url http://docker.cin.su:8011 `
|
||||
--base-id <base-id-from-project-context> `
|
||||
--extension <extension-name> `
|
||||
--object-type CommonForm `
|
||||
@@ -712,7 +745,7 @@ Agent working-view report:
|
||||
|
||||
```powershell
|
||||
python scripts\report_1c_agent_working_view.py `
|
||||
--adapter-url http://docker-gpu.cin.su:8011 `
|
||||
--adapter-url http://docker.cin.su:8011 `
|
||||
--base-id <base-id-from-project-context> `
|
||||
--extension <extension-name> `
|
||||
--object-type CommonForm `
|
||||
@@ -738,7 +771,7 @@ Optional live selector-chain smoke against a real adapter/base:
|
||||
python scripts\smoke_1c_mcp_selector_chain.py `
|
||||
--live `
|
||||
--transport rest `
|
||||
--adapter-url http://docker-gpu.cin.su:8011 `
|
||||
--adapter-url http://docker.cin.su:8011 `
|
||||
--base-id <base-id-from-project-context> `
|
||||
--json `
|
||||
--no-report
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
# Adapter Observer
|
||||
|
||||
`adapter-observer` is an independent, read-only web service for adapter telemetry.
|
||||
It must never connect to 1C SQL, call write methods, modify `/data`, or be a
|
||||
runtime dependency of `adapter-1c-rest` or `adapter-1c-mcp`.
|
||||
|
||||
## Inputs and guarantees
|
||||
|
||||
- It mounts REST and MCP Docker volumes at `/audit:ro` and `/mcp-audit:ro` and
|
||||
reads only rotated `adapter-audit.jsonl*` / `mcp-audit.jsonl*` files.
|
||||
- The source events contain public selector summaries, status/error, request
|
||||
correlation id, timings, and safe result summaries. They intentionally omit
|
||||
BSL text, raw SQL payloads, storage keys, and credentials.
|
||||
- A missing or malformed log is an Observer condition, never an adapter error.
|
||||
- The UI distinguishes `exception` from expected safe outcomes such as
|
||||
`blocked`, `unsupported`, and `invalid_argument`.
|
||||
|
||||
## Tabs
|
||||
|
||||
- **Журнал запросов**: filterable individual REST events and their safe details.
|
||||
- **Аналитика**: p50/p95, slow method ranking, grouped non-OK fingerprints and
|
||||
evidence-based next actions.
|
||||
- **Покрытие**: contract for adding a future `metadata.adapter.audit` snapshot.
|
||||
|
||||
## Retry boundary
|
||||
|
||||
Audit JSONL deliberately has no complete request payload. Do not reconstruct
|
||||
or guess it from selector summaries. A future replay button may repeat only a
|
||||
request captured by Observer itself with an explicit read-only allowlist. It
|
||||
must never replay a write request from logs.
|
||||
|
||||
## Deployment
|
||||
|
||||
On `docker.cin.su`, inspect the existing REST container and volume first:
|
||||
|
||||
```text
|
||||
docker inspect adapter-1c-rest
|
||||
docker volume ls
|
||||
```
|
||||
|
||||
Deploy only the Observer stack from `core/deploy/docker/adapter-observer`.
|
||||
It uses external volume `adapter-1c_adapter-1c-data` read-only and port 8031
|
||||
by default. Do not run `down` against the adapter compose project.
|
||||
|
||||
```text
|
||||
docker compose --env-file .env -f compose.yaml up -d --build
|
||||
curl http://localhost:8031/health
|
||||
```
|
||||
|
||||
## Development contract
|
||||
|
||||
When adapter telemetry changes, preserve backwards parsing: unknown fields are
|
||||
shown in event details; known metrics remain optional. Before adding a special
|
||||
visualization, record its input schema and add fixture JSONL tests. The general
|
||||
journal must continue working for unknown adapter methods.
|
||||
|
||||
## Key decisions for future agents
|
||||
|
||||
1. Keep Observer a separate Compose project, port, image and failure domain.
|
||||
Never add it as a dependency to REST/MCP and never restart those containers
|
||||
while deploying it.
|
||||
2. The volumes are externally named `adapter-1c_adapter-1c-data` and
|
||||
`adapter-1c-mcp_adapter-1c-mcp-data` on `docker.cin.su`; Observer mounts
|
||||
them only as `/audit:ro` and `/mcp-audit:ro`.
|
||||
3. Audit events are evidence, not replay payloads. The historical journal can
|
||||
link to an object by a public selector but cannot reconstruct omitted fields.
|
||||
4. Treat job lifecycle statuses `accepted`, `running`, `done` and `cancelled`
|
||||
as operational state, not failures. Expected rejections are shown separately
|
||||
from adapter exceptions.
|
||||
5. Retain both `duration_ms` (Observer-facing REST wall time) and optional
|
||||
result timings. Do not manufacture nested timings if the adapter did not
|
||||
return them; the first precise per-span waterfall requires an Observer-owned
|
||||
read-only proxy/session trace.
|
||||
|
||||
## Delivery plan
|
||||
|
||||
### Delivered MVP
|
||||
|
||||
- Rotated REST JSONL reader, safe event details and filters.
|
||||
- p50/p95/max latency by method, error fingerprint grouping and guidance.
|
||||
- Read-only Docker deployment and health endpoint.
|
||||
- MCP-to-REST request correlation by `request_id`; absent REST pair is shown as
|
||||
a transport boundary, not a decoder failure.
|
||||
- A bounded read-only coverage refresh (`help.methods` and
|
||||
`metadata.adapter.audit`) stored in Observer's own state volume, with the
|
||||
latest 50 snapshots per base and a schema/method/count delta.
|
||||
- A best-effort background coverage snapshot every 900 seconds for `upo_test`.
|
||||
It has a 300-second minimum interval and must never affect UI availability.
|
||||
|
||||
### Next safe increments
|
||||
|
||||
1. Add an Observer-owned, read-only allowlist proxy. It can save complete
|
||||
*sanitized* read request payloads for user-initiated replay and form a true
|
||||
parent/child waterfall; no historical write replay.
|
||||
2. Periodically invoke `metadata.adapter.audit` through that proxy and persist
|
||||
versioned coverage snapshots in an Observer-owned SQLite database.
|
||||
3. Add regression screens: compare coverage/schema/method catalog before and
|
||||
after an adapter release, with an explicit `not comparable` state.
|
||||
4. Add retention, export and role controls before exposing the journal outside
|
||||
the internal network.
|
||||
|
||||
### Explicit non-goals until separately approved
|
||||
|
||||
- No replay of historical write, activation, repository or password operations.
|
||||
- No raw request/response capture solely to make replay convenient.
|
||||
- No automatic remediation, SQL optimization, Configurator launch or mutation.
|
||||
- No claim that a slow request is an adapter defect without repeated evidence.
|
||||
|
||||
### Items commonly forgotten in observability work
|
||||
|
||||
- **Clock semantics:** preserve source UTC timestamp and Observer receive time;
|
||||
never compare durations across hosts as if clocks were synchronized.
|
||||
- **Asynchrony:** display job poll calls separately from end-to-end job time;
|
||||
polling volume must not dominate failure charts.
|
||||
- **Cardinality:** bound selector/error fingerprints so arbitrary object names
|
||||
cannot create an unbounded metrics index.
|
||||
- **Release correlation:** record Observer version and adapter health/method
|
||||
catalog snapshot beside each periodic coverage run.
|
||||
- **Availability:** health, disk/read errors and log rotation failures of
|
||||
Observer must appear in its own diagnostics, not as adapter failures.
|
||||
- **Retention and access:** define data lifetime, exported fields and viewer
|
||||
permissions before making port 8031 internet-facing or adding Caddy routes.
|
||||
|
||||
## Review checklist
|
||||
|
||||
- Does a proposed feature work with omitted payload fields rather than guessing?
|
||||
- Does it classify expected rejection separately from exception/transport loss?
|
||||
- Does it preserve `request_id` and public selector provenance?
|
||||
- Is every newly persisted field redacted and bounded by retention?
|
||||
- Can the Observer be stopped without affecting adapter requests?
|
||||
@@ -0,0 +1,50 @@
|
||||
# Прикладной эталон дополнительных реквизитов для разработки
|
||||
|
||||
## Цель
|
||||
|
||||
Опубликовать в изолированной базе `upo_test` read-only endpoint, который
|
||||
читает ПВХ средствами платформы 1С. Это эталон для разработки и регрессионного
|
||||
сравнения SQL-адаптера; адаптер к endpoint не подключается.
|
||||
|
||||
## Изолированное расширение
|
||||
|
||||
1. В Конфигураторе создайте новое расширение `AdapterAdditionalAttributesBridge`.
|
||||
2. Добавьте серверный общий модуль `ДополнительныеРеквизитыReadOnly` и вставьте
|
||||
содержимое `plugins/1c/bridge/additional_attributes_readonly.bsl`.
|
||||
3. Добавьте HTTP-сервис `AdapterRuntimeBridge` с URL `/runtime-bridge`.
|
||||
4. Добавьте URL-шаблон `rpc`, метод `POST`, и обработчик
|
||||
`ДополнительныеРеквизитыHTTP.ОбработатьRPC`.
|
||||
5. Добавьте серверный модуль `ДополнительныеРеквизитыHTTP` и вставьте
|
||||
`plugins/1c/bridge/additional_attributes_http_handler.bsl`.
|
||||
6. Ограничьте публикацию тестовой сетью и отдельным техническим пользователем
|
||||
только с правами чтения ПВХ и справочника `СтруктурныеЕдиницы` вместе с его
|
||||
табличной частью `ДополнительныеРеквизиты`.
|
||||
7. Обновите конфигурацию базы из расширения и опубликуйте HTTP-сервис.
|
||||
|
||||
Не меняйте существующие `Chatbot`, биллинг, телефонию или сервисы обмена.
|
||||
|
||||
## Проверка endpoint
|
||||
|
||||
```json
|
||||
{"method":"additional_attributes.find","payload":{"base_id":"upo_test","query":"Ответственное направление","include_deleted":false,"limit":10}}
|
||||
```
|
||||
|
||||
Ожидается `status=found` и как минимум `ref`, `description`,
|
||||
`identifier_for_formula`, `property_set`, `value_type`.
|
||||
|
||||
Затем:
|
||||
|
||||
```json
|
||||
{"method":"additional_attributes.storage.resolve","payload":{"base_id":"upo_test","property_ref":"<UUID свойства>","owner_ref":"Catalog.СтруктурныеЕдиницы"}}
|
||||
```
|
||||
|
||||
Ожидается `status=confirmed` и источник
|
||||
`Справочник.СтруктурныеЕдиницы.ДополнительныеРеквизиты`.
|
||||
|
||||
## Использование результата
|
||||
|
||||
Сравните ответ endpoint с `data.list`, `data.schema`,
|
||||
`additional_attributes.find` и `additional_attributes.storage.resolve` SQL
|
||||
адаптера. В код адаптера переносятся только подтверждённые общие правила
|
||||
разбора метаданных и SQL-маршрута; адрес endpoint, учётные данные и вызовы
|
||||
платформы в адаптер не добавляются.
|
||||
Reference in New Issue
Block a user