# 1C Operational Coding Loop

Цель: сделать помощника по 1С, который работает в темпе реальной разработки, без постоянной полной выгрузки конфигурации в XML и без обязательного 1C:EDT на первом этапе.

## Problem

Полная выгрузка конфигурации в XML медленная. 1C:EDT требует отдельной установки, настройки проекта и дисциплины синхронизации. Для оперативной разработки помощнику нужны текущие данные почти сразу:

- структура базы и конфигурации;
- доступные объекты, реквизиты, табличные части, формы и команды;
- актуальные модули BSL;
- примеры реальных данных для read-only анализа;
- постановки задач из текста, Excel-файлов и скриншотов интерфейса.

## Decision

Используем двухконтурную схему.

Быстрый контур:

- read-only SQL для диагностики, выборок и проверки данных;
- легкий 1C agent внутри базы или рядом с ней для метаданных, модулей и управляемых операций;
- локальный кеш/snapshot с коротким TTL;
- RAG поверх актуального кеша;
- генерация патчей, внешних отчетов, обработок и расширений как артефактов.

Тяжелый контур:

- XML/EDT/хранилище конфигурации для периодической полной синхронизации;
- сборка, ревью, массовый рефакторинг и долгоживущие изменения;
- финальная проверка перед переносом в production.

## Live Sources

### SQL Read-Only

SQL удобен как быстрый источник данных, но не является главным источником метаданных 1С.

Разрешено:

- read-only запросы;
- выборки для отчетов и сверок;
- оценка объемов данных;
- поиск аномалий;
- проверка результата после изменения в тестовой базе.

Запрещено:

- DML/DDL;
- изменение таблиц платформы напрямую;
- запись в production;
- хранение строк подключения и паролей в репозитории.

## 1C Storage Layers (write/read boundary)

Принцип работы со слоями конфигурации:

- `Config` и `ConfigCAS` — **active** (уже применённое в системе состояние). Для них разрешены только read-операции.
- `ConfigSave` и `ConfigCASSave` — **saved, not yet applied** (сохранённое в конфигураторе состояние). Это целевые слои для формирования изменений через адаптер.
- Base-изменения пишутся в `ConfigSave`.
- Extension-изменения пишутся в `ConfigCASSave`.

Жёсткое правило:

- Коннектор/агент не пишет в `Config`/`ConfigCAS`.
- Изменения должны идти через saved-слои и проходить сравнение `ConfigSave↔Config`, `ConfigCASSave↔ConfigCAS` до ручного/внешнего apply в production.
- Если требуется production apply, это отдельный человеческий процесс контроля и проверки.

Мини-чеклист перед передачей на manual-apply:

1. Есть актуальный compare saved-vs-active.
2. Есть report с изменениями по объектам.
3. Есть отметка «не применено» в `ConfigSave`/`ConfigCASSave`.
4. Есть rollback-план и явное human approval.

### Working-State Read Policy

Для программирования и оперативного анализа помощник должен читать последнее
сохранённое состояние конфигуратора, а не только применённую конфигурацию.

По умолчанию:

- MCP-запросы к `extension.objects.find`, `modules.search`, `code.search` и
  `metadata.resolve_overrides` используют `source_state=working`;
- REST-запросы к тем же методам используют `state=working`;
- `working` означает: сначала `ConfigSave`/`ConfigCASSave`, затем active-слой
  как fallback;
- результаты помечаются `activation_state`: `saved_only`, `saved_override` или
  `active`.

Когда нужно сравнение:

- `source_state=applied` / `state=active` — показать только применённое;
- `source_state=all` / `state=both` — показать оба слоя и различия;
- `full_scan=true` включается только осознанно для глубокого поиска по active
  `ConfigCAS`, потому что такой поиск медленнее.

Если пользователь спрашивает естественным языком вроде «выдай список всех форм
в save только имена», агент должен трактовать это как working/save-first
срез, вернуть имена saved-форм и не отбрасывать объекты `saved_only`: они могут
быть ещё не активированы и всё равно являются текущим состоянием разработки.

### Code Write Policy

Для оперативного программирования агент не должен знать SQL-нюансы: таблицы,
имена файлов, stream indexes и brace paths являются внутренней реализацией
адаптера.

Стандартный write API для агента:

- `code.write` с `module_text`/`full_text`/`code` заменяет весь модуль;
- `code.write` с `routine_name` и `routine_text` заменяет только указанную
  процедуру или функцию;
- `code.write` с `old` и `new` заменяет фрагмент только если `old` найден
  ровно один раз в выбранной области. Если передан `routine_name` или
  canonical path до процедуры, областью является эта процедура/функция; иначе
  весь текущий saved-модуль.

По умолчанию `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
`saved_state`, activation_state `not_activated`, production_apply `false`.
Ответы `code.read` и `code.search` для saved-кода содержат `current_state`:
source `saved_state`, activation_state `not_activated`.
Для `code.read`: `state=working` означает save-first с fallback в active,
`state=save` читает только saved layer, `state=active` пропускает saved layer.
`state=both` читает saved и active отдельно, возвращает `layers` для обоих
слоев и `comparison.both_present` / `comparison.differs`. При `include_text=true`
верхнеуровневый `text` берется из saved-state, если он есть, иначе из active;
`text_source` показывает выбранный слой.
Для `code.search state=both` saved-совпадения идут первыми, active-слой
добирается отдельным проходом, а `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`, и не
записывает.

### 1C Agent

Для актуальной структуры и кода нужен небольшой агент на стороне 1С. Он может быть реализован как внешняя обработка, расширение или опубликованный HTTP-сервис.

Минимальные функции:

- вернуть список объектов метаданных;
- вернуть описание объекта: реквизиты, табличные части, формы, команды, модули;
- искать по BSL-модулям;
- читать текст выбранного модуля;
- выполнять read-only запрос языка запросов 1С с лимитами;
- отдавать версию/дату изменения конфигурации для инвалидации кеша;
- принимать change proposal, но не применять его автоматически в production.

Если публикация HTTP-сервиса невозможна, первым вариантом может быть ручной запуск внешней обработки, которая выгружает JSON snapshot в общую папку.

## Freshness Model

Модель не должна считать кеш вечным.

- SQL read-only данные читаются по запросу.
- Metadata snapshot имеет TTL и номер версии конфигурации.
- BSL-модули кешируются с checksum.
- Перед генерацией кода под конкретный объект помощник проверяет свежесть metadata snapshot.
- Если snapshot устарел или отсутствует, помощник запрашивает обновление через agent.

## Task Intake

Задачи приходят разными форматами:

- обычный текст;
- Excel-файл с требованиями, примером отчета или справочником полей;
- скриншот формы, нарисованный макет интерфейса или ошибка;
- фрагмент BSL;
- SQL/запрос 1С;
- описание бизнес-процесса.

Обработка:

- текст идет напрямую в модель;
- Excel парсится структурно: листы, заголовки, таблицы, примечания;
- скриншоты идут через vision/OCR и превращаются в описание интерфейса, полей, команд и ошибок;
- все извлеченные требования связываются с metadata snapshot и BSL search.

## Coding Outputs

Помощник должен уметь выдавать:

- BSL-фрагмент;
- полный модуль формы/объекта/общего модуля;
- текст запроса 1С;
- схему внешнего отчета или обработки;
- proposal для расширения;
- список изменений по формам и командам;
- чеклист проверки;
- тестовые сценарии;
- rollback plan.

Для production изменения не применяются напрямую. Нормальный поток:

1. Получить актуальные метаданные и модули.
2. Сформировать change proposal.
3. Проверить синтаксис и зависимости в тестовой базе.
4. Сформировать артефакт: внешняя обработка, отчет, расширение или патч.
5. Провести ревью.
6. Перенести через согласованный 1С-процесс.

## Practical Priority

Первый рабочий MVP:

1. SQL read-only connector с валидацией политики.
2. 1C metadata snapshot через внешнюю обработку в JSON.
3. BSL module snapshot и поиск по модулям.
4. Загрузка постановки из Excel.
5. Загрузка скриншота формы/ошибки в vision-модель.
6. Генерация внешнего отчета/обработки по актуальному snapshot.
7. Smoke eval: модель не выдумывает реквизиты и просит обновить snapshot, если он устарел.
