Initial project import

This commit is contained in:
2026-08-14 09:40:51 +03:00
parent 00040e5ce4
commit d7099bf80d
146 changed files with 30509 additions and 1055 deletions
+121 -1
View File
@@ -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.
+132
View File
@@ -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 -
```
+11 -5
View File
@@ -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> `
+124
View File
@@ -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-адаптер не открывает формы, не запускает
обработчики и не выдаёт созданные СКД во время работы элементы за статические
метаданные.
+1 -1
View File
@@ -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 `
+61 -2
View File
@@ -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`, и не
+37 -4
View File
@@ -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
+130
View File
@@ -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, учётные данные и вызовы
платформы в адаптер не добавляются.