# 1C Adapter API Contract

## Configuration repository control

The adapter is SQL-only. It does not start, automate, emulate, or require
Designer/Configurator. Configurator actions are performed by a human and are
outside the adapter's execution boundary.

Repository operations are available through `repository.status`,
`repository.lock.plan`, `repository.lock`, `repository.lock.confirm`, `repository.lock.verify`,
`repository.unlock`, `repository.commit.plan`, and `repository.commit`.
Configuration is selected only by `payload.base_id`. No Designer executable,
Configurator endpoint, bridge, or external 1C runner is used by this adapter.

`repository.lock_mode=manual` is selected per base. Manual mode requires no
Designer integration:
`repository.lock.plan` returns the exact public development-object names to
lock in Configurator, and `repository.lock.confirm` records the user's explicit
confirmation for only that object set. Such a session is marked
`user_confirmation_only`; `repository.lock.verify` returns
`manual_confirmation_unverified` and never represents it as an automatic
repository check.

In the SQL-only release, `repository.lock.request` persists an adapter-side
coordination request with status `pending_user_lock` and the resolved public
object scope. It deliberately does not write a marker into the 1C infobase SQL
database: such a marker would not create a native repository lock.
`repository.lock.request.status` exposes the request state, and
`repository.lock.confirm` can consume its `request_id`; confirmation always
uses the immutable object set stored in the request.

Manual requests expire after `ONEC_REPOSITORY_REQUEST_TTL_SECONDS` (24 hours
by default) and may be explicitly cancelled with
`repository.lock.request.cancel`. Manual confirmations expire after
`ONEC_REPOSITORY_CONFIRMATION_TTL_SECONDS` (2 hours by default). After the user
releases the objects in Configurator, `repository.lock.close` closes the
confirmation, marks its originating request as `closed`, and immediately blocks
further SQL writes through that session.
The adapter keeps a bounded audit trail of request creation, confirmation,
cancellation, and closure and exposes it to the administrative requests view.

Repository coordination records only the human-confirmed scope. It does not
invoke Designer repository commands and does not prove a native lock. For
configured bases, saved-state apply is blocked until the caller supplies an
active adapter-owned `lock_session_id`; that session is coordination evidence,
not a native repository transaction.

Status: draft, read-only first.

Related work plan: `docs/1c-extension-layer-plan.md`.

## Source Boundaries

- The live adapter works with 1C through SQL storage only.
- XML exports and Form.xml files may be used by this project for analysis,
  fixtures, learning, diffing, and rule discovery, but they are not a live 1C
  write transport for the adapter.
- When XML-derived rules are promoted into the adapter, the runtime write path
  must still resolve to concrete SQL storage targets such as `ConfigSave` or
  `ConfigCASSave`, with explicit gates and readback verification.
- SQL readback is not proof that Configurator accepts, displays, saves, or
  activates a change. Only a human may provide that confirmation.
- The adapter must reject, rather than fabricate, a `ConfigSave`/
  `ConfigCASSave` container or signature whose SQL protocol is not proven.

## Data Composition Schema Query Writes

`scd.patch` changes only one proven direct XML scalar: a dataset `query`, a
calculated-field `expression`, or a resource `expression`.  It resolves the
report and SCD by public 1C names, preserves the surrounding SQL payload bytes
and compression envelope, and never accepts a storage key from a caller.

For `execution_mode=apply`, `apply_and_verify`, or `apply_and_rollback`, a
missing saved SCD payload is prepared internally through the established
saved-state copy codec when `allow_sql_saved_state_prepare=true` is supplied.
The caller still explicitly authorizes the eventual edit with
`allow_saved_state_write=true` and `allow_sql_saved_state_apply=true`.

`apply_and_rollback` additionally requires
`allow_sql_saved_state_rollback=true`. It restores the exact apply backup, not
a newly encoded copy of the old query. If this request created the saved-state
overlay, it then removes only receipt-recorded rows after hash precondition
checks. A successful result proves SQL readback and cleanup only; it does not
prove Configurator visibility, acceptance, or activation.

## Report And Appearance Inspection

`report.inspect` is the read-only entry point for a report component graph.
It accepts a public report name and returns only independently confirmed
modules, forms, templates and SCD components. A form called
`ОсновнаяСхемаКомпоновкиДанных` remains a form; it is never promoted to an SCD
from its name or a guessed `.0` stream.

```json
{"method":"report.inspect","payload":{"base_id":"upo_test","report":"ФинансовыйРезультат"}}
```

`appearance.inspect` is a separate read-only reader for a base
`CommonTemplate` whose `.0` carrier has the `AppearanceTemplate` XML root.
This selects the payload reader only; it does not establish the 1C platform
`TemplateType`, which remains unresolved until its metadata descriptor is
decoded.
It returns raw XML-backed `item(parameter, value)` rules and an XML tag
outline. It has no write operation, and an extension template returns explicit
`unsupported` until an extension carrier route is proven.

```json
{"method":"appearance.inspect","payload":{"base_id":"upo_test","template":"ОформлениеОтчетовБежевый"}}
```

## User Identity And Access Terminology

An unqualified user request means an **infobase user**: the platform identity
visible in Configurator under administration of infobase users. It does not
mean the BSP `Catalog.Пользователи` record.

The two layers are intentionally separate:

| Layer | Canonical term | Source | Authoritative for |
|---|---|---|---|
| Platform | `infobase_user` / Configurator user | `dbo.v8users` and the 1C `ПользователиИнформационнойБазы` runtime API | login identity, authentication flags, platform administrator flag, assigned platform role set and exact platform roles |
| Application | `bsp_catalog_user` / BSP user | BSP catalogs, access groups, profiles and access registers | BSP membership, profiles, access groups, RLS/access-key chains and application access diagnostics |

Routing rules:

- ordinary "users", "user roles", "login", "password", and "Configurator
  users" start with `infobase.users.search` or `infobase.user.get`;
- explicit BSP/group/profile/RLS questions use `access.users.search` and
  `access.user.explain`;
- a name match between the layers is correlation only and never proves that
  the records are identical;
- BSP groups/profiles must never be reported as the exact role assignment of a
  Configurator user;
- SQL `RolesID` proves the assigned platform role-set identity, but exact role
  names require the supported 1C runtime `ПользователиИнформационнойБазы` API.

`infobase.users.search` and `infobase.user.get` expose only safe `v8users`
fields: platform id, name/full name, change time, login-list visibility,
authentication-presence flags, administrator flag, `RolesID`, and protected
payload size. They never expose `Data`, password hashes, password-policy blobs,
material keys, or raw `users.usr` content. Until a runtime connector is added,
responses set `role_assignment.exact_role_names_status=runtime_required`.

### Configurator User Password Operations

Password mutation is available only for the platform `infobase_user` layer:

- `infobase.user.password.capabilities` reports whether the protected path is
  ready for a concrete `base_id`;
- `infobase.user.password.status` reports `empty`, `set`, or
  `standard_authentication_disabled` for one exact platform user. It never
  returns password hashes or `Data`;
- `infobase.user.password.set` changes the password and requires `user`, the
  exact 32-hex `confirm_user_id`, `new_password`, and
  `allow_password_change=true`. It uses the same guarded SQL transaction as
  `clear`, storing the Base64-encoded SHA-1 pairs for the UTF-8 password and
  its Unicode uppercase form;
- `infobase.user.password.clear` removes the password and requires `user`, the
  exact 32-hex `confirm_user_id`, and `allow_password_clear=true`; it rejects a
  `new_password` field. This operation uses SQL only: it locks the exact
  `dbo.v8users` row, decodes that row's XOR-protected `Data` container, replaces
  only the first adjacent current SHA-1/Base64 password pair with the empty
  password pair, writes with an old-`Data` concurrency predicate, and verifies
  readback before commit;
- a platform administrator additionally requires
  `allow_administrator_password_change=true`;
- normally both mutations are blocked when `ONEC_ADAPTER_SERVICE_TOKEN` is
  empty. A disposable isolated test stand may explicitly set
  `ONEC_INFOBASE_USER_ADMIN_ALLOW_UNAUTHENTICATED=true`; it affects only this
  isolated SQL operation and must never be enabled in production.

Both operations select the exact user through `infobase.user.get` and update
only `dbo.v8users.Data`. The clear-text password for `set` exists only in memory
and is not included in responses or write-history payloads. Neither operation
updates `Params/users.usr`, `EAuth`, roles, administrator flags, names, or BSP
records. Both are rejected when `EAuth=0`, because changing stored hashes does
not enable standard authentication.

The SQL path decodes the row-specific XOR key, patches the first adjacent
current-hash pair in the authentication section, reuses the original key and
byte layout, and verifies that no other parsed scalar changed. A compare-old
`Data` predicate prevents overwriting a concurrent user edit.

Successful `set` and `clear` results are recorded in
`metadata.write.history` with method, operation, target platform id/name,
transport, status, and verification result. Request payloads are never written
to that history, so `new_password` is not persisted. The SQL layout with 43
root fields was live-verified on `upo` during an administrator password clear.

## Undecoded Evidence

Public decode/read methods should not drop useful payload evidence when a
decoder is incomplete or when only part of the object is understood. Methods
that expose decoded object, part, form, or template payloads accept:

```json
{
  "evidence_mode": "none | summary | full | raw"
}
```

Contract:

- `none`: omit `undecoded_evidence`;
- `summary`: return compact role, markers, root signature, string samples,
  payload size/hash metadata, and safe stream/base64 samples;
- `full`: return broader samples and longer text excerpts for agent-side
  analysis;
- `raw`: return the broadest evidence. Low-level storage offsets, block hashes,
  and physical coordinates are exposed only together with
  `include_storage=true`.

`evidence_mode` is intentionally separate from object `view`
(`effective/base/extension`): `view` selects the metadata layer, while
`evidence_mode` selects diagnostic detail.

Current methods using this contract include `metadata.object.decode`,
`metadata.object.parts`, `metadata.object.templates`,
`metadata.object.template.details`, `metadata.form.decode`, and
`metadata.object.full` through its `parts_summary` section.

## Template Content Export

`templates.read` can return the actual current SQL-stored template content in
read-only mode. Content is opt-in and bounded:

```json
{
  "base_id": "upo_test",
  "kind": "Document",
  "name": "АктВыполненныхРабот",
  "template": "ПФ_MXL_УдалитьАкт",
  "view": "full",
  "include_content": true,
  "max_content_bytes": 262144
}
```

Each part then contains `content_export.container`: media type, decoded byte
size, returned byte size, SHA1, truncation flag, and base64 data. Embedded
decodable stream/base64 blocks are also returned in `extracted_text`, including
HTML as text. `max_content_bytes` is limited to 1 MiB per returned item. The
operation never writes to 1C SQL tables; it only reads the configured database
and does not perform platform rendering to pixels or PDF.

## Managed Form Element Types

SQL form profiles distinguish every element type observed in the reference UPO
`Form.xml` export. This includes containers, pages, command bars, tables and
their search additions, input/label fields, checkbox fields, picture fields,
radio-button fields, spreadsheet-document fields, label/picture decorations,
context menus, and extended tooltips. XML is used only to confirm decoding
rules; live `metadata.form.decode` and `metadata.object.form.details` responses
are produced from SQL payloads. Dynamic table profiles also expose confirmed
read-only/skip-on-input and row-set-change flags, height in table rows, footer,
row-selection mode, horizontal/vertical line flags, alternating-row color,
automatic row insertion, and drag-start/drag flags. The dynamic-table SQL
positions for `ChangeRowSet`, `HeightInTableRows`, and `Footer` were confirmed
against four distinct tables in the reference UPO form. Rare layout and
behavior properties remain best-effort until their SQL positions are confirmed
by multiple samples.

The complete offline UPO inventory covers 6,748 `Form.xml` files, all parsed
successfully, with 821,077 named elements, 41 element kinds, and 253 direct
property tags. Runtime still does not read XML. SQL section records additionally
decode `CurrentRowUse` and `ModifiesSavedData` for commands and `MainAttribute`
and `SavedData` for attributes. The confirmed payload positions are 9/10 for
command current-row/saved-data behavior and 10/11 for attribute main/saved-data
flags. Table search, view-status, and search-control additions expose a
structured `AdditionSource` derived from their decoded parent-table hierarchy.
Its public value contains `owner_element` and `representation`; it deliberately
does not use the reserved `table` key, which denotes physical SQL storage in
adapter diagnostics. Command-bar buttons also decode `ButtonImportance`,
`GroupHorizontalAlign`, and `GroupVerticalAlign`; label/picture decorations
decode both group-alignment properties. Input, label, picture, checkbox,
radio-button, and chart fields use positions 53/54. Marker-22 containers
(ordinary/column groups, command bars, and pages) use the last three and last
two entries of their variable-length node (`-3/-2`). The other confirmed SQL
positions are 11/41/42 for command-bar buttons and 32/33 for decorations. The
vertical enum is normalized as `Top`, `Center`, `Bottom`, or `Auto`. These
positions were cross-checked against contrasting XML declarations and the
matching live SQL payloads; XML remains an offline analysis source only.

Marker-35/37 fields also expose `AutoEditMode=true` when position 26 is
`EnterOnInput`. In the reference document-form inventory, all 11,817 explicit
`AutoEditMode` declarations are paired with `EditMode=EnterOnInput`; the live
SQL payload stores that pair as the single enum code `2` at position 26. The
adapter therefore returns `AutoEditMode` as a confirmed derived property and
does not invent a separate storage position.

Table additions have two confirmed live SQL layouts: marker-5 records embedded
directly in the variable tail of a dynamic list/table, and the same records
embedded in its auto command bar. Both are normalized to public marker-6
addition items. Validation against 12 UPO forms decoded 39/39 XML-declared
search-string, view-status, and search-control additions with a resolved owner
and representation.

The variable-length dynamic-table tail is decoded relative to the end of the
element after validating the four table-addition records. It exposes
`SearchStringLocation`, `ViewStatusLocation`, `SearchControlLocation`, and
`FileDragMode`. The location enum maps were confirmed against eleven live SQL
tables covering `Default`, `None`, `CommandBar`, `Top`, `FormCaption`, and
`PullFromTop` values present in the reference XML export.

Related-form discovery follows the object descriptor layout for reports
(`Form` section 5), data processors (section 6), exchange plans (section 6),
selection criteria (section 3), and settings storages (section 4). Report and
data-processor template sections are kept separate from forms.

Live descriptor routes are also confirmed for enum forms (section 3),
information-register forms (section 5), accumulation-register forms (section
8), business-process and task forms (section 4), chart-of-characteristic-types
and chart-of-calculation-types forms (section 7), chart-of-accounts forms
(section 6), and document-journal forms (section 6). Form list and full SQL
payload decoding were verified by public object/form names for every listed
kind. Template sections for exchange plans and charts of characteristic types
remain distinct at section 4.

Template discovery additionally covers enum templates (section 4),
information-register templates (section 6), and document-journal templates
(section 3). Catalog templates use section 3; section 4 is the catalog command
section. Safe bounded `templates.read include_content=true` export was verified
for the newly routed kinds, including MOXCEL content.

Object-command discovery is confirmed for catalogs (section 4), documents
(section 6), data processors and document journals (section 5), accumulation
registers (section 4), information registers and tasks (section 8), and
exchange plans and reports (section 7). When a separate command descriptor is
absent, the public command identity falls back to the identity embedded in the
owner descriptor. Results are deduplicated by command GUID/name.

## Payload Diff

`payload.diff` is a diagnostic method for comparing two payload snapshots. It
accepts `before` and `after` sources as either live storage pointers
(`base_id`, `table`, `file_name`) or inline payloads (`payload_base64`,
`payload_hex`, or `text`). It requires `diagnostic=true`.

The response includes:

- byte sizes and SHA1 hashes;
- decoded envelope metadata;
- unified text diff;
- scalar brace-tree changes with paths such as `$.1.3.2`;
- changed string sequence entries;
- compact `undecoded_evidence` for both sides when `include_evidence=true`.

Use it after manual Designer/configurator edits to identify which raw payload
nodes changed before promoting a rule into a higher-level decoder or writer.

## Agent-Facing Addressing

Agent-facing selectors and answers should prefer full semantic 1C paths over
GUIDs, SQL names, CAS keys, or bare local names.

Default path shape:

```text
<ObjectKind>.<ObjectName>[.<Section>.<Member>...]
```

Examples:

```text
Справочник.Контрагенты
Справочник.Контрагенты.Наименование
Документ.РеализацияТоваровУслуг.Товары.Номенклатура
РегистрСведений.ЦеныНоменклатуры.Ресурсы.Цена
Документ.РеализацияТоваровУслуг.Форма.ФормаДокумента.Товары
ОбщийМодуль.ИнтеграцияСCRM.ОтправитьКонтрагента
```

Short names are allowed as input conveniences, but adapter methods must either
normalize them to one `canonical_path` or return ambiguity candidates. Bare
member names such as `Наименование` are not safe write targets without object,
form, module, or routine context.

Metadata path resolution and BSL symbol resolution are separate operations. A
code expression such as `Номенклатура.ЕдИзмерения.Код` starts from a local
symbol until the adapter proves that the symbol maps to a metadata path or
typed value.

Resolved agent-facing objects should include, where known:

- `canonical_path`;
- `context_path` when a short path was resolved inside a known context;
- `path_kind`, for example `metadata_object`, `metadata_member`,
  `form_element`, `module`, `routine`, or `code_symbol`;
- `presentation` and synonym;
- `ref` and GUID/storage evidence for internal follow-up calls.

## BSL Symbol Resolution

Command:

```text
python scripts/resolve_1c_bsl_symbol.py
  --metadata <metadata snapshot>
  --modules <bsl module snapshot>
  --expression <BSL expression>
  --module-id <module-id>
  [--object-kind <Kind>]
  [--object-name <Name>]
  [--routine-name <Routine>]
```

Safe Unicode command shape:

```text
python scripts/resolve_1c_bsl_symbol.py
  --metadata <metadata snapshot>
  --modules <bsl module snapshot>
  --expression-b64 <utf8-base64 BSL expression>
```

Output schema:

```text
onec_bsl_symbol_resolution.v1
```

Live adapter RPC:

```json
{
  "method": "code.symbol.resolve",
  "payload": {
    "base_id": "<base-id>",
    "expression": "Номенклатура.ЕдИзмерение.Код",
    "module_ref": "<module-ref-from-code-search>",
    "routine_name": "<routine-name>"
  }
}
```

Purpose:

- resolve BSL expressions inside a concrete module/routine/form context before
  treating them as metadata paths;
- return `metadata_path` only for full semantic paths such as
  `Справочник.Номенклатура.Артикул` or for members proven by context, for
  example a current object-module standard attribute;
- in live adapter mode, read the module through `modules.read`, use
  `metadata.definition.find` for full metadata paths, and use
  `metadata.object.attributes` for context-proven owner members;
- return `parameter` or `local_variable` when the first expression segment is
  declared in the current routine/module, with `safe_as_metadata_path=false`;
- return unresolved candidates for short object names such as
  `Номенклатура.ЕдИзмерение.Код` instead of silently converting them to
  `Справочник.Номенклатура...`.

## Agent-Facing Code Writes

Normal coding agents should write BSL through `code.write`, not through SQL,
storage rows, payload paths, or `metadata.module.write_apply`.

### Working-state and saved-state ownership rule

An agent works only with the effective working configuration. It reads the
saved development version when one exists and otherwise reads active code; it
does not determine whether `ConfigSave` or `ConfigCASSave` exists, and it must
not call a separate saved-state preparation operation as part of a normal code
change.

The adapter owns that transition. A `concrete_reference` returned by
`code.search` or `code.read` is an opaque proof of the exact active module
stream, extension, and layer—not permission to write active storage. After
preflight has resolved that reference, a normal apply must do the following
inside the adapter:

`code.write` accepts the same reference either directly or inside
`write_selector.target`. It verifies the supplied public `ref`, extension,
layer, canonical path, and routine against that exact stream, then routes it
to the saved copy without repeating `metadata.object.modules` discovery.

For one apply request the adapter authorizes the public extension scope once
before preparation. Its internal prepare and writer stages reuse that
process-bound authorization; they must not repeat broad support/owner scans
against `ConfigCASSave`. If authorization is absent, the request returns a
bounded `blocked` result before any support scan or saved-state mutation.

For a client transport deadline shorter than the adapter write budget, call
`adapter.job.start` with `method="code.write"` and the same public payload,
then poll `adapter.job.get`. This preserves the final structured write result;
the caller must not retry the SQL write while the job is running.

1. verify the exact extension/layer and active source stream;
2. create a missing saved-state copy through the proven
   `Config`/`ConfigCAS` to `ConfigSave`/`ConfigCASSave` copy path;
3. apply the guarded edit only to saved state;
4. make a backup, validate BSL and the old-text/SHA preconditions, and reread
   the written result; and
5. support the requested rollback, including removal or restoration of any
   adapter-created saved-state copy.

`metadata.write.preflight` reports such a first write as `status=ready` with
`route.preparation.status=adapter_managed`,
`route.preparation.caller_action_required=false`, and
`saved_state.status=will_prepare`. `needs_prepare` is reserved for a route
whose copy protocol is not sufficiently proven for the adapter to execute.
The adapter must never write the active `Config` or `ConfigCAS` layer.

For a hash-keyed extension overlay, this internal copy includes the live
extension root manifest (`root_cas_key`) and the confirmed object parts. An
exact BSL stream reference remains the write target, but must not reduce the
saved-state preparation to that leaf stream alone.

The caller never invokes or waits for this preparation as a separate workflow:
`code.write` performs the bounded copy, write, verification, and readback as
one adapter operation. Its preparation gate is restricted to the confirmed
extension layer and public object; it does not perform a whole-extension
support scan before copying proven source parts.


`code.write` accepts 1C names and code text:

```json
{
  "method": "code.write",
  "payload": {
    "base_id": "upo_test",
    "object_type": "CommonForm",
    "object_name": "t_Форма",
    "routine_name": "ЗаменаДомена",
    "routine_text": "Процедура ЗаменаДомена(Команда)\n\t// code\nКонецПроцедуры\n"
  }
}
```

Contract:

- default `mode` is `plan` and does not write SQL. `apply`,
  `apply_and_verify`, and `apply_and_rollback` must be requested explicitly;
  they save only to the working `ConfigSave`/`ConfigCASSave` layer, not to
  production;
- every `code.write` response includes `write_mode.target=saved_state`,
  `write_mode.activation_state=not_activated`, and
  `write_mode.production_apply=false`;
- the normal agent view is the effective working configuration: a matching
  `ConfigSave`/`ConfigCASSave` overlay is read ahead of active code. Agents
  do not inspect, create, or select saved-state rows. For a first extension
  write, the adapter takes the exact active module handle returned by
  `code.search`/`code.read`, creates the proven `ConfigCASSave` copy inside
  its guarded apply workflow, and then edits that copy. Preflight reports this
  as `route.preparation.status=adapter_managed`; no separate prepare call is
  required from the agent.
- saved-state `code.read` and `code.search` responses include
  `current_state.source=saved_state` and
  `current_state.activation_state=not_activated`;
- `code.read state=working` is save-first with active fallback,
  `state=save` reads only the saved layer, and `state=active` skips saved-state
  lookup. `state=both` returns side-by-side layers and comparison metadata;
- SQL/storage gates are set by the adapter for this facade;
- public responses hide physical storage details unless
  `include_storage=true`;
- full module replacement uses `module_text`, `full_text`, or `code`;
- routine replacement uses `routine_name` plus `routine_text`;
- fragment replacement uses `old` plus `new`. With `routine_name` or a
  routine-level `canonical_path`, `old` must occur exactly once inside that
  procedure/function; without routine scope, it must occur exactly once in the
  current saved module text;
- if the fragment is missing or repeated, the adapter returns
  `fragment_not_found` or `ambiguous_fragment` and does not write;
- low-level storage methods remain diagnostic and implementation details.

For embedded form modules the adapter writes only the scalar module token in
the saved form payload with `path_preserve_format`. Whole-form payload
canonicalization is forbidden because Designer may reject the form even if the
payload decoder can parse it. A write plan for such a container returns a ready
`code.write` apply hint rather than asking the caller to invent a
`#stream:<index>`.

## Resolve Object

Command:

```text
python scripts/resolve_1c_object.py
  --kind <ConfiguratorKind>
  --name <ConfiguratorName>
  --index <unified object route index>
```

Output schema:

```text
onec_object_resolution.v1
```

Purpose:

- resolve objects by configurator-visible names, synonyms, qualified names, and
  generated type names such as `DocumentRef.ПриходнаяНакладная`;
- return the canonical base object plus extension overlays;
- keep storage details under `storage`, so agent-facing code can continue to
  operate with 1C metadata names.

## Object Brief Context

Command:

```text
python scripts/get_1c_object_brief_context.py
  --index <unified object route index>
  --kind <Kind>
  --name <Name>
  --view effective|base|extension
  --extension <ExtensionName>
  --max-attributes <N>
  --max-modules <N>
  --max-forms <N>
  --output <json>
```

Output schema:

```text
onec_object_brief_context.v1
```

Purpose:

- provide the default starting context for an agent after a user names a 1C
  object;
- keep the response compact: object identity, active extension names,
  attribute summary, tabular section names, form list, module list, overlay
  counts, and suggested follow-up tools;
- use `view=effective` by default so the agent sees the same working object
  picture as the user sees in Configurator;
- avoid loading large BSL modules or full form XML; use the dedicated module
  and form APIs for detail reads.

## Fact Resolution

Command:

```text
python scripts/resolve_1c_fact.py
  --index <unified object route index>
  --path <Kind.Name.Member>
  --view effective|base|extension
  [--extension <ExtensionName>]
```

Safe Unicode command shape for agents and PowerShell callers:

```text
python scripts/resolve_1c_fact.py
  --index <unified object route index>
  --path-b64 <utf8-base64 Kind.Name.Member>
```

Output schema:

```text
onec_fact_resolution.v1
```

Purpose:

- verify concrete facts about the current configuration before code generation,
  for example `Справочник.Номенклатура.Цвет` or
  `Документ.ПриходнаяНакладная.ДатаСоздания`;
- return `exists`, `confidence`, `area`, object identity, matched member, and
  source provenance;
- support object, attribute, tabular section, form, module, and snapshot-backed
  checks through the same agent-facing shape;
- keep examples and old snapshots out of the current-configuration path unless
  the caller explicitly passes `--snapshot`.
- normalize positive results to a full `canonical_path` and return ambiguity
  candidates when a short path is not unique.

Policy:

- RAG may explain platform behavior, patterns, and documentation.
- `resolve_1c_fact.py` or a richer adapter method must confirm concrete object,
  attribute, form, command, module, and data facts for the selected base.
- A negative result means the fact is not confirmed in the provided source; the
  agent should not silently replace it with a fact from examples or generic
  documentation.

## Live Adapter Navigation

The REST adapter exposes metadata and BSL navigation through public selectors.
Agents should prefer these selectors over diagnostic storage fields.

Object selectors:

- Object-scoped methods accept the same public selector shapes: `ref`,
  `kind` + `name`, `guid`, or MCP-friendly aliases `object_type`,
  `object_name`, and `object_guid`.
- `ref` can be a public qualified object reference such as
  `Обработка.<Name>` or `Document.<Name>`; the adapter normalizes Russian and
  English metadata kind names to the canonical internal kind.
- Public selectors returned by the adapter keep backward-compatible
  `kind`/`name`/`guid` fields and, when `kind` + `name` are known, also include
  compact `ref=<canonical-kind>.<metadata-object-name>`.
- Do not branch on concrete object names in client or MCP code. Normalize the
  selector once and pass the resulting public selector through adapter methods.
- If a result contains `read_selector.method`, call that method with the
  selector payload as-is. Do not reconstruct the selector from display text or
  storage diagnostics.
- `help.methods` exposes `selector_capabilities` for object-scoped methods.
  Agents should use these flags instead of inferring selector behavior only from
  natural-language descriptions.

Important methods:

- `data.schema`, `data.list`, `data.get`, `data.count`, and `data.query` expose
  logical 1C data names while reading physical SQL tables. The object is
  selected by public `ref` (for example `Catalog.<Name>`) or by
  `kind` + `name`; a row is selected separately with `record_ref`. SQL table
  names and `_Fld...` columns are internal routes, not caller-facing selectors.
  Constants are exposed as a typed `value`; enumeration rows include their
  public value `name`, `synonym`, and `value_ref`. Business-process storage is
  resolved through the platform `_BPr<N>` route internally.
- `additional_attributes.find` is the read-only entry point for additional
  requisites. It reads a public `ChartOfCharacteristicTypes` selector (by
  default `ДополнительныеРеквизитыИСведения`) and accepts `query` for a
  description search. Its `empty_source` result is deliberately distinct from
  `not_found`: it means that the resolved chart route has no records in the
  selected infobase, so no property reference or value type can be claimed.
  `additional_attributes.storage.resolve` requires a confirmed 32-hex
  `property_ref` plus `owner_ref`. It searches candidate information registers
  and returns a storage join only after live metadata proves dimensions
  `Объект` and `Свойство`, a `Значение` resource, and that `Свойство` is a
  reference to the selected PВХ. Otherwise it returns `unresolved` and never
  guesses physical `_Fld...` columns.
  For `ChartOfCharacteristicTypes`, `data.schema` and the find result also
  expose chart-level `allowed_value_types` with its live Config evidence path.
  This is explicitly not represented as the type of an individual property
  record until that record's `ТипЗначения` route is decoded.
- `data.present` returns a compact presentation for one `record_ref`, and
  `data.movements` reads register rows for a `recorder_ref`.
- BSP access-key methods use the same object/record separation without
  overloading their query-area selector. For `access.keys.query`,
  `kind=object` selects the access-key area; it is not a metadata kind. Pass
  `object_ref=Справочники.Номенклатура` to select the metadata object and
  `record_ref=<32-hex-ref>` to select one application-data record.
  `access.object_keys.resolve` and `access.object.explain` accept the same
  pair. The adapter resolves `object_sql_number` and `object_id` internally.
  Raw `object`, `object_id`, and `object_sql_number` remain compatibility
  filters only and should not be generated by new MCP clients.
- `metadata.adapter.audit include_unmapped=true` distinguishes configuration
  metadata from platform storage. Live `upo_test` DBNames verification showed
  the former candidate roles `Acoustic`, `Bots`, `Ecs`, `LangModel`,
  `MobileClientDataExchange`, `STT*`, `URLExternalData`, and
  `WebSocketClients` as singleton platform-system tables, normally carrying
  the zero GUID. `ExtDataSrcPrms` is auxiliary external-data-source storage.
  None of these roles is exposed as an invented public metadata kind.
- `metadata.object.form.details` decodes form commands with their explicit
  `Action` value from the SQL form payload. `command_links` resolve that action
  against the complete form-module routine index; `routines_sample` is only a
  compact preview and never limits handler resolution.
- The same form details expose command `CurrentRowUse`/`ModifiesSavedData`,
  attribute `MainAttribute`/`SavedData`, and structured table-addition
  `AdditionSource` properties directly from the live SQL payload. Command-bar
  buttons additionally expose `ButtonImportance`, `GroupHorizontalAlign`, and
  `GroupVerticalAlign`; decorations expose their horizontal and vertical group
  alignment.
- `metadata.object.special.details` for `ScheduledJob` returns the complete
  decoded SQL schedule: date/time windows, completion interval, intraday
  repeat and pause, weekdays, day/month restrictions, months, and week/day
  repeat periods, together with use/predefined and restart settings. It also
  resolves the handler's common module and returns `handler.read_selector`;
  pass that selector unchanged to `modules.read` to read the exact procedure.
  A non-predefined disabled job may legitimately return
  `schedule.status=not_configured` when its separate `.0` schedule payload is
  absent.
- `metadata.object.properties` has kind-specific live SQL decoders for
  `EventSubscription`, `WebService`, and `HTTPService`. Event subscriptions
  expose their source objects, event and handler. The handler contains a
  `read_selector` that can be passed unchanged to `modules.read` to obtain the
  exact common-module procedure from the working SQL state. Web services expose namespace,
  XDTO packages, descriptor/session settings, operations and parameters; HTTP
  services expose root URL/session settings, URL templates, HTTP methods and
  handlers. XML exports are used only to establish and test the Config layout,
  never as a runtime data source.
- The same property API has SQL decoders for `CommonAttribute`,
  `SessionParameter`, `FunctionalOption`, and `FunctionalOptionsParameter`.
  Common attributes expose their value type, content objects, indexing,
  full-text/history flags, and data-separation settings and references.
  Session parameters preserve composite types, including unions of several
  reference or platform types. Functional options expose their storage
  location, privileged-get flag, and every affected top-level or nested
  metadata object; functional-option parameters expose every `Use` target.
  XML exports are used only to learn field layout and verify names—the runtime
  values and references always come from the selected SQL base.
- `CommonCommand`, `SettingsStorage`, and `Subsystem` also have dedicated
  SQL-only property decoders. Common commands expose their public command
  group (including standard platform groups), parameter type, and a ready
  command-module read selector. Settings storages expose all four default and
  auxiliary save/load form roles plus every owned form; every physical module
  stream is classified as a manager module. Subsystems expose use/help/command
  interface flags, picture, content, child subsystems, and references decoded
  from the separate command-interface SQL part.
- A normal `metadata.objects.list` request validates a kind-specific cache hit
  against the current configuration-root object count. If a root-discovered
  kind is only partially represented in the local metadata cache, the adapter
  ignores that cache hit and returns the authoritative live SQL list. This
  prevents a partial `DBNames` cache from hiding root-only objects such as
  settings storages. The check and fallback are read-only for the 1C database.
- `Language`, `CommonPicture`, `Style`, and `StyleItem` are decoded from live
  SQL as first-class metadata. Languages expose their language code. Common
  pictures expose choice/appearance flags and a bounded binary summary from
  their `.0` part (format, byte count, and hash) without returning unbounded
  binary data. Recognized formats include PNG, JPEG, GIF, BMP, ICO, SVG, and
  zipped 1C picture packages. Styles expose every value from the separate
  style table; style items decode absolute/web/standard colors, font
  attributes, and borders. Unknown platform codes remain explicit with
  `status=unknown_code`.
- `XDTOPackage` reads its live `.0` XML payload and returns namespace/form
  settings, imports, object/value types, nested anonymous type definitions,
  properties, constraints, and enumeration values. `WSReference` reads the
  location and generated manager identifiers from its main payload, then
  decodes the `.0` stream container into WSDL and XSD. The public result links
  messages, port-type operations, SOAP actions, bindings, services, ports,
  addresses, schemas, types, elements, restrictions, and enumerations. XML
  files exported by Configurator are not used at runtime.
- `ExternalDataSource` is decoded from the live SQL `Config` hierarchy. The
  source returns its tables, cubes, and functions; each table has a public
  name-based ref, its `NameInDataSource`, key fields, and the complete field
  collection with SQL name, 1C value type, `ReadOnly`, and `AllowNull`.
  Primitive number, string, date, boolean, and binary (`R`) patterns are
  decoded without consulting the Configurator export. Child payloads are read
  in one batch. The adapter only describes connection metadata and never opens
  or changes the external system itself. Live `upo_test` validation decoded
  30 tables, 733/733 typed fields, and 53 key-field links; this base contains
  no cubes or functions.
- A top-level `CommonTemplate.<Name>` is a direct template selector for
  `metadata.object.template.details`, `templates.read`, `templates.analyze`,
  and `templates.map`. It is resolved internally to the Config GUID and its
  payload parts, while the public response preserves the `CommonTemplate`
  name and ref. This is distinct from a template nested under another metadata
  object and no longer returns an empty template collection.
- `DefinedType.<Name>` is decoded completely from its live SQL `Config`
  payload. `metadata.object.special.details` returns the identity, comment,
  union value type, and every constituent type. Generated platform types are
  resolved internally to their owning metadata object, including business
  process object/ref/selection/list/route-point variants and constant value
  managers. A full `upo_test` audit read all 612 payloads and resolved all
  1,943 unique type GUIDs across 6,241 type references; no unresolved type
  remained.
- `SelectionCriterion.<Name>` returns its value type, standard-command flag,
  default forms, list presentation, and complete metadata content collection.
  Content GUIDs are translated to public name-based refs. Live validation
  decoded all 11 criteria and resolved 862/862 content references.
- `Enum.<Name>` returns its identity, comment, standard-command and quick-choice
  settings, choice mode, and ordered values with public refs. Multilingual and
  empty-synonym identities are supported. Live validation decoded 1,226 enums
  and 9,380/9,380 values.
- `metadata.object.modules`, `modules.read`, and `code.read` support
  `WebService`, `HTTPService`, and `IntegrationService`. Service Config parts
  can contain both the complete BSL module and a short repeated fragment; the
  adapter selects the largest canonical BSL stream and exposes one public
  module as `Модуль Web-сервиса`, `Модуль HTTP-сервиса`, or
  `Модуль сервиса интеграции`. Handler names decoded by
  `metadata.object.properties` can therefore be followed directly into their
  live SQL module routines.
- Exact extension objects are resolved by the same public `kind` + `name` or
  `ref` selectors as base objects. `metadata.object.modules` also returns form
  modules owned by the selected object, with qualified 1C names and without
  exposing CAS keys unless `include_storage=true`.
- The same module APIs expose the four configuration-level modules through the
  public `Configuration.<Name>` selector: ordinary application, external
  connection, managed application, and session. Runtime discovery reads the
  embedded Configuration identity GUID from the current SQL root descriptor
  and maps its `.0`, `.5`, `.6`, and `.7` parts internally. An intentionally
  empty external-connection module is returned as an empty module instead of
  being treated as missing. XML is not consulted at runtime.
- `CommonModule.<Name>` is a complete public SQL route: root discovery resolves
  the object by its 1C name, and module APIs read its canonical `.0` Config
  stream. Live `upo_test` discovery contains 3,400 common modules; sampled
  modules decoded as BSL with public routine indexes. In public
  `metadata.object.modules`, `modules.read`, and code-search responses, the
  module `name`, `qualified_name`, and `display_name` are the 1C common-module
  name; Config GUID/stream coordinates are exposed only with
  `include_storage=true`. Common-module writes, when explicitly enabled, remain
  limited to the saved-state layer.
- For `Role`, `metadata.object.properties` reads the separate SQL part
  `<role-guid>.0` and returns `set_for_new_objects`,
  `set_for_attributes_by_default`, `independent_rights_of_child_objects`,
  object and child-object rights, per-right RLS conditions, and full
  restriction templates. Standard right GUIDs are translated to public 1C
  names; unrecognized platform GUIDs remain visible with
  `status=unknown_right_guid` instead of being silently discarded. Permission
  targets are resolved to public names even when the target is nested and has
  no standalone `Config` file: `_Fld`/`_VT` routes are joined through the
  read-only SQL schema to their parent object, object commands are found in the
  parent's SQL `Config` payload, and integration channels are matched to the
  decoded `IntegrationService` channel list. The resulting refs use public
  paths such as `Catalog.<Name>.Attribute.<Name>`,
  `DataProcessor.<Name>.Command.<Name>`, and
  `IntegrationService.<Name>.Channel.<Name>`. These decoded identities may be
  cached only in the adapter's local save index; the live 1C SQL database is
  never modified.
- For `DocumentJournal`, pass `include_column_types=true` to resolve every
  public column type through the referenced document attributes. This is a
  supported deep read, not an undecoded property; prefer `adapter.job.start`
  because large journals may require a long metadata scan.
- `data.virtual` supports `СрезПоследних`/`СрезПервых` for periodic information
  registers and `Остатки`/`Обороты`/`ОстаткиИОбороты` for accumulation
  registers. Exact dimension values are passed in `filters`. At least one
  dimension filter is required by default; an intentional broad query must set
  `allow_full_scan=true`. Accounting-register totals are reported as
  `unsupported_register` until account and subconto semantics are resolved for
  the selected register; raw movements remain readable.
- All `data.*` methods publish the same name-first object selector contract in
  `help.methods`: callers may pass `object_ref`, `ref`, `kind/name/guid`, or
  MCP aliases `object_type/object_name/object_guid`. Prefer `object_ref` with a
  1C code-style name such as `Справочники.Номенклатура` when a method also
  needs `record_ref` or `recorder_ref`; a record reference is never treated as
  the metadata object name.
- `metadata.support.decode` accepts the same optional public object selectors.
  When `ref` or `kind/name` is supplied, the adapter resolves the metadata GUID
  internally and returns the effective supplier-support rule for that named
  object; callers do not need to discover or pass storage identifiers.
- `metadata.code_index.build` accepts an optional public object selector for a
  targeted local-cache refresh. A named base object is resolved through
  `Config`, while an extension object with `extension_guid` uses
  `ConfigCASSave`; omitting the selector keeps the bounded global `ConfigCAS`
  scan. The index remains a local derivative cache and never writes to the 1C
  database.
- `metadata.form.owner_index.build` keeps the owner selector and form selector
  separate. Use `ref`/`kind/name` for the owning 1C object and `form` or
  `form_name` for the nested form. For example,
  `ref=Обработки.ОбменДанными, form_name=ФормаОбмена` searches the owner by
  `ОбменДанными` and indexes only the named form; the two names are not
  substituted for one another.
- `metadata.form.command_button.verify` uses the same owner/form separation and
  publishes `command_name`, `button_name`, and `handler_name` as distinct child
  selectors. Saved forms with the same name are matched against owner evidence
  before verification, so a form belonging to another object is not selected
  merely because its form name matches.
- `metadata.form.command_button.write` uses the same disambiguation before it
  creates any proposal. `object_guid` identifies the owner and `form_guid`
  identifies the nested form; they are never substituted for one another.
  Name-first selection does not weaken the write workflow:
  `allow_saved_state_write`, repository apply checks,
  `allow_sql_saved_state_apply`, verification, and rollback guards remain
  mandatory in their existing execution modes.
- `metadata.form.write_target.resolve` and
  `metadata.form.write_target.verify` use the same public hierarchy:
  `ref`/`kind`/`name` select the form owner, `form_name`/`form_guid` select the
  nested form, and `element`/`command`/`attribute` select the child target.
  `object_guid` is never treated as a nested `form_guid`.
- `metadata.form.element.write` and `metadata.form.element.write_apply` pass
  that hierarchy through unchanged when resolving the concrete saved-state
  file. An owner `object_guid` therefore cannot bypass name-first form
  resolution. Planning remains opt-in and SQL apply still requires the
  existing explicit apply and repository gates.
- `metadata.form.target.move` resolves the owner and nested form before it
  resolves `from_element` and `to_element`; structural moves cannot select a
  same-named form owned by another object. Its write/apply/rollback gates are
  unchanged.
- `metadata.form.write_matrix.build` and `.smoke` use the same owner/form
  hierarchy while resolving the saved-state file. The build operation is
  read-only; smoke remains restricted to explicit apply-and-rollback.
- `metadata.write_learning.capture_before` and `.capture_after` also accept
  the public owner `ref` plus a separate form selector. They write only local
  learning artifacts; the selected 1C saved-state file is read, not modified.
- `metadata.saved_state.forms.search` accepts the same owner scope and filters
  decoded saved forms by owner evidence before returning them. Its default
  response contains the public owner ref, form name, qualified name, and
  name-based selectors for matched elements, commands, and attributes.
  SQL files, GUID identities, internal brace paths/markers, and low-level
  writable-property coordinates are returned only with
  `include_storage=true`. `form_guid` remains a separate exact diagnostic
  selector. The method is read-only.
- `metadata.saved_state.modules.search` accepts a public module-owner `ref` or
  `kind/name/guid`, resolves the internal saved-state prefix itself, and keeps
  `file_name`/`stream_index` as opt-in diagnostic selectors. By default its
  rows contain only the 1C owner, nested form, module role, qualified name,
  source preview, and a semantic selector. SQL tables/files, GUID identities,
  hashes, `module_ref`, and low-level write targets are returned only with
  `include_storage=true`. Callers do not need to know an owner GUID for
  name-first saved-code search.
- `code.write` is the public name-first write facade. It accepts the module
  owner through `ref` or `kind/name/guid`, with `routine_name` as a separate
  child selector, then resolves the concrete saved-state module internally.
  `metadata.module.write_apply` remains the guarded low-level apply operation
  for an already resolved `module_ref`.
- `templates.areas.find` accepts a public template ref such as
  `Макеты.ПечатнаяФорма`. The template selector, named-area selector
  (`area_name`/`area_query`), and low-level direct route are separate MCP
  fields; normal callers do not need a storage route.
- `metadata.cache.lookup` accepts a public object `ref` in addition to
  `kind/name/guid`. Its result is only a local identity-cache hint; operations
  that require current evidence still verify against live SQL.
- `metadata.module_owner_cache.prune` accepts the cached module owner through
  a public `ref` or `kind/name` and resolves `owner_guid` internally. Generated
  `module_ref` values remain valid for narrow follow-up cleanup. The method
  changes only adapter-local SQLite state, never the 1C SQL database; use
  `dry_run=true` to inspect the matching count without deleting cache rows.
- `metadata.module_owner_cache.backfill` incrementally builds the reverse
  `module_ref -> 1C object` map from current metadata. One call processes a
  bounded object page and returns `next_cursor` (`kind_index`, `kind`,
  `offset`) until `complete=true`. The discovered public owner is propagated
  to existing lexical/vector code-index rows, including the matching
  active/saved-state table pair. By default objects without code-index rows
  are skipped so the operation remains fast; `deep=true` explicitly enables
  their slower metadata-module decoding. This changes adapter-local SQLite
  only.
- For backward compatibility, a legacy bare `guid` without an owner selector
  may still identify a form. `object_guid` never gets that legacy treatment.
- Logical schema results are cached briefly. `refresh_cache=true` forces a live
  metadata decode after a configuration change.
- `metadata.objects.list`: lists base/effective metadata objects only. It must
  not be used with `extension`; extension-scoped queries such as `test2` must
  use `extension.objects.find` or `metadata.definition.find` with `extension`.
- `extension.objects.find`: lists extension objects from the current working
  programming view by default. `state=working` overlays saved rows from
  `ConfigCASSave` over applied extension rows; saved-only objects are returned
  too. Results carry `activation_state`: `active`, `saved_override`, or
  `saved_only`.
- `metadata.definition.find`: resolves public names and references such as
  `Обработка.<Name>` or `Document.<Name>`. When exactly one metadata object is
  found, it is promoted to top-level `object`; `related_selectors` lists the
  next safe calls allowed by the object's capabilities, including card/full
  reads and scoped `code.search`/`modules.search` selectors for module-capable
  objects. These related selectors include `ref` when the object kind and name
  are known.
- `modules.search`: searches decoded BSL and returns matches with
  `read_selector.method="modules.read"`. The selector may contain an opaque
  `module_ref`; pass it through unchanged. When the owner is resolved, the same
  selector also includes the public owner fields such as `kind`, `name`, `guid`,
  and `ref`. For extension programming, `state=working` is the default and
  searches `ConfigCASSave` first, including saved-only form/module payloads that
  are not applied yet. Use `state=active` only when intentionally checking the
  applied extension; use `full_scan=true` only for broad active `ConfigCAS`
  fallback scans.
- `modules.read`: reads a module by public object selector or opaque
  `module_ref`. Public responses include `origin` layer evidence even when the
  owner object is not fully recovered: `Config` means applied configuration,
  `ConfigSave` means base saved state, `ConfigCASSave` means saved state that
  still needs owner/layer evidence for base-vs-extension choice, and unresolved
  `ConfigCAS` means `cas_reference` with
  `write_surface=requires_owner_resolution`.
- `code.search`: agent-facing search wrapper. Items contain
  `read_selector.method="code.read"` and can be read directly by `code.read`.
  If `modules.search` resolved the owner, `code.search` preserves the owner
  selector fields while changing the read method to `code.read`. Scoped calls
  may pass `module_ordinal` together with `ref`/`kind`/`name`/`guid`; the
  response must stay a public `onec_code_search.v1` object. Items also carry
  public `origin` evidence from `modules.search`, so the agent can see base,
  saved-state, extension, or unresolved CAS provenance before reading the full
  code fragment. `state` is passed through to `modules.search`; the MCP
  `source_state=working` policy maps to this `state=working` mode.
- `code.read`: wraps module/routine reads for agent-facing code analysis. It
  accepts either `routine_name` or the inclusive `line_start`/`line_end` range.
  A focused request may use a previously decoded local code-index snapshot
  (`source.kind=code_index_cache`, `freshness.verified_against_sql=false`) to
  avoid decoding a large container again; pass `prefer_code_index=false` for
  the normal live SQL decode path. It must preserve the module `origin`
  evidence from `modules.read` so write planning can still distinguish base,
  saved state, extension, or unresolved CAS references.
- `metadata.adapter.audit`: reports recognized metadata kinds, public kind
  counts, missing supported kinds, and unmapped DBNames roles. Every
  `missing_supported_kinds` item has
  `presence_status=supported_absent_in_selected_base`: absence in one
  infobase is not reported as absence of adapter implementation.
- Base root discovery includes the configuration object itself, command groups,
  document numerators, external data sources, and integration services. The
  root collection UUID map is verified against object UUIDs from the XML export
  rather than inferred from collection position alone.

Generate a reproducible live coverage matrix without putting adapter or SQL
credentials in a file:

```text
python scripts/audit_1c_adapter_coverage.py --base-id upo_test --output reports/1c-adapter-coverage.json
```

The script reads the adapter bearer token from `ONEC_ADAPTER_TOKEN` and never
reads or prints the SQL password.

Rare-kind regression fixtures for `CalculationRegister`, `Sequence`, and the
legacy `Interface` are checked separately:

```text
python scripts/check_1c_metadata_kind_fixtures.py --live
```

The fixture checker is read-only. It reuses one extension saved-state query for
all fixtures on the same layer and does not force the slower active
`ConfigCAS` full scan. It requires an explicit dedicated-base mapping for
`Interface`.
The manifest pins the required Designer version and an external structural
reference. Use `scripts/export_1c_extension_sources.ps1` to export `test2`
through operating-system integrated authentication; the helper accepts no
infobase user or credential parameters and never writes platform SQL.

For a resumable read-only application-data audit, run the full public chain for
one object of every data-bearing metadata kind:

```text
python scripts/audit_1c_adapter_coverage.py
  --base-id upo_test
  --sample-data-reads
  --workers 2
  --timeout 90
  --checkpoint reports/1c-adapter-data-checkpoint.json
  --output reports/1c-adapter-coverage-live.json
```

The checkpoint is replaced atomically after every completed operation and
kind. Resume an
interrupted run with `--resume`; add `--retry-degraded` to rerun only timed-out
or failed kinds while retaining successful evidence. Each data check records
durations and statuses for `data.schema`, `data.list`, `data.count`, and
`data.get` when the sampled row has a public reference key. Register rows with
no reference key report `data.get=not_applicable` rather than a false failure.

An XML export can be used as an independent property-schema reference. The
analyzer keeps the base configuration and each extension as separate layers:

```text
python scripts/analyze_1c_xml_metadata.py --include-artifacts --output reports/1c-xml-metadata-analysis.json
```

Owner resolution:

- Search results distinguish module readability from owner resolution. A module
  can be readable through `read_selector` even when `owner.status=unresolved`.
- Use `counts.owner_resolved`, `counts.owner_unresolved`,
  `counts.owner_scan_limit_hit`, and `diagnostics.owner_resolution` to decide
  whether to narrow the selector or increase `owner_scan_limit`.
- Do not request `include_storage=true` only to read a found module; use the
  public `read_selector` first.

Working source state:

- For programming/designer analysis, agents must query the latest saved working
  state first. In MCP calls this is `source_state=working`; the MCP bridge maps
  it to REST `state=working` for `extension.objects.find`, `modules.search`,
  `code.search`, and `metadata.resolve_overrides`.
- `source_state=applied` maps to REST `state=active` and intentionally ignores
  saved rows. Use it only when checking what is already applied.
- `source_state=all` maps to REST `state=both` for side-by-side inspection. The
  response keeps `activation_state` markers such as `saved_only`,
  `saved_override`, and `active`, so agents can tell which findings are not
  applied yet.
- `code.read state=both` reads the saved layer and the active layer as two
  independent views. The response sets `current_state.source=both`, returns
  ordered `layers` entries for `saved_state` and `active`, and includes
  `comparison.both_present` plus `comparison.differs`. When `include_text=true`,
  top-level `text` is the effective programming text: saved-state text if it
  exists, otherwise active text. `text_source` names the layer used.
  For extension form modules, the saved counterpart is matched by extension,
  logical form owner, and form GUID rather than by assuming identical active
  and saved CAS file names.
- `code.search state=both` also returns a mixed view for saved CommonForm code:
  saved-state matches are listed first, active matches are fetched with an
  independent `state=active` pass, and `counts.saved_matches` /
  `counts.active_matches` show layer coverage.
- If an extension object exists only in `ConfigCASSave`, it is still part of the
  working programming surface. Analysis and write planning must not discard it
  just because activation has not happened yet.

Smoke check:

```text
python scripts/smoke_1c_mcp_selector_chain.py --json
```

This offline smoke validates generic MCP selector chains such as
`metadata.definition.find -> related_selectors.code_search -> code.search ->
item.read_selector -> code.read`. Examples use placeholders only and must not
contain concrete configuration object names.

Optional live smoke:

```text
python scripts/smoke_1c_mcp_selector_chain.py
  --live
  --transport rest
  --adapter-url <1c-rest-adapter-url>
  --base-id <base-id-from-project-context>
  --json
```

To run the same live chain through the MCP proxy instead of direct REST `/rpc`,
use:

```text
python scripts/smoke_1c_mcp_selector_chain.py
  --live
  --transport mcp
  --mcp-url <1c-mcp-proxy-url>
  --base-id <base-id-from-project-context>
  --json
```

The live mode discovers a module-capable metadata object through
`metadata.objects.list`, verifies `metadata.definition.find` related selectors,
reads the first module through `modules.read` using the returned selector,
then derives a search token from module text or routine metadata and verifies
`code.search -> item.read_selector -> code.read`. It does not hard-code object
names or BSL fragments, and it passes object selectors through instead of
reconstructing them from display text. MCP transport performs the same calls through
`initialize`, `Mcp-Session-Id`, and `tools/call` + `onec_request`.

## Question Routing

Command:

```text
python scripts/route_1c_question.py
  --text <UserQuestionOrTask>
  --index <unified object route index>
  --view effective|base
```

Safe Unicode command shape:

```text
python scripts/route_1c_question.py
  --text-b64 <utf8-base64 UserQuestionOrTask>
  --index <unified object route index>
```

Output schema:

```text
onec_question_route.v1
```

Purpose:

- classify a user question before tools are selected;
- route documentation questions to official-docs RAG;
- route concrete configuration facts to the fact resolver;
- detect source-risk phrases such as "in the RAG example" and require current
  configuration confirmation before code generation;
- extract explicit facts such as `Справочник.Номенклатура.Артикул` and
  phrased facts such as "реквизит Артикул у справочника Номенклатура".

Routes:

- `docs_rag`: only official documentation context is needed.
- `current_config_fact`: current configuration facts are needed before answer or
  code.
- `mixed_docs_and_current_config`: use official docs for platform behavior, but
  confirm object/member facts through the adapter first.
- `needs_clarification`: neither docs nor current-config target was clear.

Contract check:

```text
python scripts/check_1c_question_router.py
  --index <unified object route index>
  --output reports/1c-question-router.json
```

## Agent Intake

Command:

```text
python scripts/build_1c_agent_intake.py
  --text <UserQuestionOrTask>
  --index <unified object route index>
  --view effective|base
```

Output schema:

```text
onec_agent_intake.v1
```

Purpose:

- create the first packet an agent should inspect before answering or writing
  code;
- include the question route, source policy, confirmed/unresolved current-base
  facts, answer/code policy, and next tool commands;
- make the "example is not current fact" rule machine-readable through
  `source_policy.examples_are_current_facts=false`;
- set `answer_policy.code_generation_allowed=false` when required current facts
  are missing or not checked.

HTTP console API:

```text
POST /api/1c/intake
{
  "question": "...",
  "source_path": "reports/1c-sql/upo/unified-object-route-index.json",
  "view": "effective"
}
```

## Saved State Object Compare

Command:

```text
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/compare_1c_saved_state_objects.ps1
  -Server <SqlServer>
  -Database <SqlDatabase>
  -User <SqlUser>
  -Password <SqlPassword>
  -Output <json>
```

Normal agent command:

```text
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/build_1c_saved_state_object_report.ps1
  -Server <SqlServer>
  -Database <SqlDatabase>
  -User <SqlUser>
  -Password <SqlPassword>
  -OutputDir <directory>
  [-SkipMarkdown]
```

Repeated observation command:

```text
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/watch_1c_saved_state_once.ps1
  -Server <SqlServer>
  -Database <SqlDatabase>
  -User <SqlUser>
  -Password <SqlPassword>
  -OutputRoot <directory>

python scripts/list_1c_saved_state_watch_runs.py
  --root <directory>
  [--limit <N>]
  [--only-with-delta]
  [--only-changed]
  --output <json>
  [--skip-markdown]
  [--skip-check]

python scripts/check_1c_saved_state_watch_run_list.py
  --list <SavedStateWatchRunListJson>
  --output <json>

python scripts/get_1c_saved_state_latest_watch_run.py
  --root <directory>
  [--require-delta]
  [--require-changed]
  --output <json>
  [--skip-markdown]
  [--skip-check]

python scripts/check_1c_saved_state_latest_watch_run.py
  --latest <SavedStateLatestWatchRunJson>
  --output <json>

python scripts/render_1c_saved_state_latest_watch_run_markdown.py
  --latest <SavedStateLatestWatchRunJson>
  --output <markdown>

python scripts/render_1c_saved_state_watch_run_list_markdown.py
  --list <SavedStateWatchRunListJson>
  --output <markdown>
```

Markdown rendering:

```text
python scripts/render_1c_saved_state_object_report_markdown.py
  --report <SavedStateObjectReportJson>
  --output <markdown>
```

Contract check:

```text
python scripts/check_1c_saved_state_object_report.py
  --report <SavedStateObjectReportJson>
  --output <json>

python scripts/check_1c_saved_state_watch_once.py
  --manifest <SavedStateWatchRunJson>
  --output <json>

python scripts/render_1c_saved_state_watch_once_markdown.py
  --manifest <SavedStateWatchRunJson>
  --output <markdown>
```

Report-to-report delta:

```text
python scripts/compare_1c_saved_state_object_reports.py
  --before <PreviousSavedStateObjectReportJson>
  --after <CurrentSavedStateObjectReportJson>
  --output <json>
  [--skip-markdown]
  [--skip-check]
```

```text
python scripts/check_1c_saved_state_object_report_delta.py
  --delta <SavedStateObjectReportDeltaJson>
  --output <json>

python scripts/render_1c_saved_state_object_report_delta_markdown.py
  --delta <SavedStateObjectReportDeltaJson>
  --output <markdown>
```

Object change lookup:

```text
python scripts/list_1c_saved_state_object_changes.py
  --report <SavedStateObjectReportJson>
  [--layer base|extension]
  [--kind <Kind>]
  [--payload-role <PayloadRole>]
  [--active-missing true|false]
  [--text-diff true|false]
  --output <json>
```

```text
python scripts/get_1c_saved_state_object_change.py
  --report <SavedStateObjectReportJson>
  --name <ConfiguratorObjectName>
  --output <json>
```

Output schema:

```text
onec_saved_state_object_comparison.v1
onec_saved_state_object_detail.v1
onec_saved_state_object_report.v1
onec_saved_state_object_report_check.v1
onec_saved_state_watch_once.v1
onec_saved_state_watch_once_check.v1
onec_saved_state_watch_run_list.v1
onec_saved_state_watch_run_list_check.v1
onec_saved_state_latest_watch_run.v1
onec_saved_state_latest_watch_run_check.v1
onec_saved_state_object_report_delta.v1
onec_saved_state_object_report_delta_check.v1
onec_saved_state_object_change_list.v1
onec_saved_state_object_change.v1
```

Purpose:

- compare saved-but-not-applied SQL state with active state in 1C object terms;
- provide a single read-only report command that runs comparison, exports the
  required payload evidence, performs object detail analysis, and writes a
  Markdown summary by default;
- provide a one-shot watch command that stores timestamped observations and
  compares the new observation with the previous one when available; it writes
  a Markdown watch summary by default unless `-SkipMarkdown` is used;
- list timestamped watch observations without reading SQL, including latest
  run, linked artifacts, check statuses, and delta counts; write Markdown next
  to the run-list JSON by default and run a contract check when `--output` is
  used;
- return the latest matching watch observation directly, with optional
  requirements for a delta or actual delta changes; run a contract check by
  default and write Markdown when `--output` is used;
- include `agent_summary` in `onec_saved_state_object_report.v1` so agents can
  see changed 1C object names, changed payload parts, active-missing part
  counts, payload roles, and short semantic term hints without parsing Markdown
  or full detail payloads;
- compare `ConfigSave` with `Config` and `ConfigCASSave` with `ConfigCAS`;
- return `object_changes` as configurator objects such as
  `ОбщийМодуль.HttpBridgeКлиент` or extension forms;
- keep `FileName`, byte sizes, and hashes under storage evidence;
- keep `root`, `versions`, and extension `configinfo` under `system_changes`.
- optionally analyze changed object payloads for text deltas, added/removed
  words, and saved form/module string samples.
- when extension manifest summary and active `ConfigCAS` export are provided,
  detail analysis resolves extension saved parts to active CAS keys before
  comparing payloads.
- render the JSON report as compact Markdown for human review while keeping 1C
  configurator names first and SQL storage names as evidence.
- classify changed payload parts with roles such as `bsl_module_text`,
  `form_descriptor`, `form_body`, `primary_payload`, or `metadata_payload`;
- keep raw word diffs in detail evidence, but expose filtered
  `semantic_hints.added_terms` and `semantic_hints.removed_terms` for agent
  triage.
- validate each generated report with a final read-only contract check before
  treating it as reliable agent input.
- compare two saved-state report observations by 1C object name and stable
  payload-part fingerprints when the user continues editing between checks;
  write Markdown and a contract-check JSON next to the delta JSON by default
  when `--output` is used.
- support object-level lookup from a saved-state report by full name, short
  configurator name, synonym, suffix, or contains match; ambiguous matches must
  return candidates rather than selecting one silently.
- support compact changed-object listing and filtering by layer, kind,
  extension, payload role, text diff presence, and missing active counterpart.

## Object Context Search

Command:

```text
python scripts/search_1c_object_context.py
  --index <unified object route index>
  --kind <Kind>
  --name <Name>
  --text <SearchText>
  --view effective|base|extension
  --extension <ExtensionName>
  --search-code
  --max-form-items <N>
  --limit <N>
  --output <json>
```

Output schema:

```text
onec_object_context_search.v1
```

Purpose:

- search one resolved object by human/configurator terms;
- search metadata attributes, tabular section names, forms, form items, form
  attributes, form commands, form events, module names, and optionally BSL code
  lines;
- keep origin/effective action evidence so matches from extensions are not
  confused with base configuration matches;
- return file paths and line snippets for code hits, allowing the agent to
  follow up with `get_1c_module.py --routine` or module snippet reads.

## Task Context Plan

Command:

```text
python scripts/plan_1c_task_context.py
  --index <unified object route index>
  --text <TaskText>
  --view effective
  --max-objects <N>
  --max-terms <N>
  --max-matches <N>
  --output <json>
```

Output schema:

```text
onec_task_context_plan.v1
```

Purpose:

- turn a user task into a read-only investigation plan;
- find likely metadata object candidates by configurator-visible object names
  and synonyms, using kind hints such as "document" or "catalog";
- for each candidate, gather brief context and search the object for task
  terms across metadata, forms, commands, events, modules, and BSL code;
- produce recommended follow-up reads such as object metadata, form context,
  and exact module reads;
- keep write support explicitly blocked by `docs/1c-write-path-safety.md`.

## Task Evidence Bundle

Command:

```text
python scripts/build_1c_task_evidence.py
  --index <unified object route index>
  --text <TaskText>
  --view effective
  --max-objects <N>
  --max-module-chars <N>
  --code-snippet-radius <N>
  --max-code-snippets <N>
  --output <json>
```

Output schema:

```text
onec_task_evidence_bundle.v1
```

Purpose:

- materialize the read-only task plan into a compact evidence bundle for code
  generation or human review;
- include full effective metadata summaries, selected form contexts, selected
  module snippets, and targeted code snippets around search hit lines;
- preserve base/extension origin, file paths, form XML paths, module paths, and
  line numbers;
- keep large files bounded by explicit limits while retaining focused evidence
  for relevant code found deep inside large modules.

## Task Change Proposal

Command:

```text
python scripts/propose_1c_task_changes.py
  --evidence <TaskEvidenceBundleJson>
  --output <json>
```

Output schema:

```text
onec_task_change_proposal.v1
```

Purpose:

- turn a read-only evidence bundle into a structured implementation proposal;
- infer broad task intents such as form command, attribute, lifecycle, or
  inspection;
- separate extension-first write candidates from base/read-only reference
  files;
- identify existing form commands/items, metadata attributes, and related code
  hits before any patch is generated;
- use full 1C paths as change targets, and keep local names only when they are
  bound to a concrete object, form, module, routine, or symbol context;
- keep the proposal under the write safety contract: no SQL/Config writes and
  no automatic production update/apply.

Markdown rendering:

```text
python scripts/render_1c_task_proposal_markdown.py
  --proposal <TaskChangeProposalJson>
  --output <markdown>
```

The Markdown report is for human review and should mirror the JSON proposal,
not replace it as machine-readable evidence.

Safety check:

```text
python scripts/check_1c_change_proposal_safety.py
  --proposal <TaskChangeProposalJson>
  --output <json>
```

Output schema:

```text
onec_change_proposal_safety_check.v1
```

Purpose:

- gate future patch generation on machine-checkable safety rules;
- require write candidates to live in the preferred extension origin and path;
- verify candidate/reference paths exist;
- reject direct SQL/Config/ConfigSave/ConfigCAS write targets;
- preserve the required gates that still block real write/apply operations.

## Patch Workspace

Command:

```text
python scripts/create_1c_patch_workspace.py
  --proposal <TaskChangeProposalJson>
  --output-root <directory>
  --slug <name>
  --output <json>
```

Output schemas:

```text
onec_patch_workspace_creation.v1
onec_patch_workspace_manifest.v1
```

Purpose:

- create a safe local workspace for future generated edits;
- run the proposal safety check before copying anything;
- copy only extension write candidates into `original/` and `working/`;
- keep `proposal.json`, `safety.json`, `manifest.json`, and `README.md`
  beside the copies;
- require edits to happen only under `working/`.

Diff command:

```text
python scripts/check_1c_patch_workspace_integrity.py
  --workspace <PatchWorkspaceDir>
  --output <json>

python scripts/check_1c_patch_source_freshness.py
  --workspace <PatchWorkspaceDir>
  --output <json>

python scripts/validate_1c_patch_workspace_semantics.py
  --workspace <PatchWorkspaceDir>
  --output <json>

python scripts/edit_1c_bsl_routine.py
  --workspace <PatchWorkspaceDir>
  --relative-path <ManifestBslModuleRelativePath>
  --operation append|replace|upsert
  --routine-text-b64 <Utf8Base64BslRoutine>
  [--keep-on-failure]
  --output <json>

python scripts/edit_1c_form_command.py
  --workspace <PatchWorkspaceDir>
  --relative-path <ManifestFormXmlRelativePath>
  --operation append|replace|upsert
  --name <CommandName>
  --title <RussianTitle>
  --action <BslHandlerName>
  [--tooltip <RussianTooltip>]
  [--id <CommandId>]
  [--keep-on-failure]
  --output <json>

python scripts/edit_1c_form_button.py
  --workspace <PatchWorkspaceDir>
  --relative-path <ManifestFormXmlRelativePath>
  --operation append|replace|upsert
  --parent-name <ParentFormItemName>
  --name <ButtonName>
  --title <RussianTitle>
  --command-name <ExistingCommandName>
  [--id <ButtonId>]
  [--keep-on-failure]
  --output <json>

python scripts/add_1c_form_button_workflow.py
  --workspace <PatchWorkspaceDir>
  --form-relative-path <ManifestFormXmlRelativePath>
  --bsl-relative-path <ManifestFormModuleRelativePath>
  --operation append|replace|upsert
  --routine-text-b64 <Utf8Base64BslRoutine>
  --command-name <CommandName>
  --command-title <RussianCommandTitle>
  --command-action <BslHandlerName>
  --button-parent-name <ParentFormItemName>
  --button-name <ButtonName>
  --button-title <RussianButtonTitle>
  [--keep-on-failure]
  --output <json>

python scripts/diff_1c_patch_workspace.py
  --workspace <PatchWorkspaceDir>
  --output <json>

python scripts/create_1c_patch_bundle.py
  --workspace <PatchWorkspaceDir>
  --output-root <PatchBundleRoot>
  --slug <BundleSlug>
  --output <json>

python scripts/check_1c_patch_bundle.py
  --bundle-dir <PatchBundleDir>
  [--zip <PatchBundleZip>]
  --output <json>

python scripts/create_1c_extension_staging_from_bundle.py
  --bundle-dir <PatchBundleDir>
  --output-root <ExtensionStagingRoot>
  --slug <StagingSlug>
  --output <json>

python scripts/check_1c_extension_staging.py
  --staging-dir <ExtensionStagingDir>
  --output <json>

python scripts/check_1c_extension_runner_config.py
  --config <ExtensionRunnerConfigJson>
  --output <json>

python scripts/create_1c_extension_validation_plan.py
  --staging-dir <ExtensionStagingDir>
  [--runner-config <SafeRunnerConfigJson>]
  --output <json>
  --markdown-output <markdown>

python scripts/create_1c_extension_validation_evidence.py
  --plan <ExtensionValidationPlanJson>
  [--output-root <EvidenceRoot>]
  --output <json>

python scripts/check_1c_extension_validation_evidence.py
  --plan <ExtensionValidationPlanJson>
  [--evidence-root <EvidenceRoot>]
  --output <json>

python scripts/check_1c_extension_validation_release.py
  --plan <ExtensionValidationPlanJson>
  [--evidence-root <EvidenceRoot>]
  --output <json>

python scripts/render_1c_extension_validation_release_markdown.py
  --release-check <ExtensionValidationReleaseCheckJson>
  --output <markdown>

python scripts/check_1c_patch_preflight.py
  --workspace <PatchWorkspaceDir>
  --output <json>

python scripts/render_1c_patch_preflight_markdown.py
  --preflight <PatchPreflightJson>
  --output <markdown>
```

Output schema:

```text
onec_patch_workspace_integrity.v1
onec_patch_source_freshness.v1
onec_patch_workspace_semantic_validation.v1
onec_bsl_routine_edit.v1
onec_form_command_edit.v1
onec_form_button_edit.v1
onec_form_button_workflow.v1
onec_patch_workspace_diff.v1
onec_patch_bundle.v1
onec_patch_bundle_creation.v1
onec_patch_bundle_check.v1
onec_extension_staging.v1
onec_extension_staging_creation.v1
onec_extension_staging_check.v1
onec_extension_runner_config_check.v1
onec_extension_validation_plan.v1
onec_extension_validation_evidence_manifest.v1
onec_extension_validation_evidence_check.v1
onec_extension_validation_release_check.v1
onec_patch_preflight.v1
```

Purpose:

- verify `original/` hashes still match the manifest before any diff/package
  step;
- verify source extension files still match the hashes recorded when the patch
  workspace was created, so stale patches cannot overwrite newer source files;
- verify every manifest file exists under both `original/` and `working/`;
- flag unexpected files under `original/` as errors and unexpected files under
  `working/` as warnings;
- validate edited workspace semantics before review: parse `Form.xml`, parse
  BSL routines, reject duplicate routines/commands, check basic BSL block
  balance, and verify form command actions have matching form-module routines
  when both files are in the workspace;
- edit one BSL procedure/function under `working/` through a manifest-bound
  operation: append a new routine, replace an existing routine, or upsert one
  routine; the command validates the full workspace semantics after writing and
  rolls the file back by default when validation fails;
- edit one form command under `working/` through a manifest-bound operation:
  append a new `<Command>`, replace an existing command, or upsert one command;
  the command chooses a safe numeric id when omitted, validates the full
  workspace semantics after writing, and rolls the file back by default when
  validation fails;
- edit one visible form button under `working/` through a manifest-bound
  operation: append/replace/upsert one `<Button>` under a named parent form item
  and bind it to an existing form command; the command validates the full
  workspace semantics after writing and rolls the file back by default when
  validation fails;
- add a complete form button workflow atomically: BSL handler, form command,
  and visible button. This is the preferred agent operation when a user asks to
  add a button. It snapshots all `working/` manifest files and restores them if
  any step fails, unless `--keep-on-failure` is explicitly set;
- compare `original/` and `working/` files from a patch workspace;
- create a review bundle only when preflight status is `ready_for_review`.
  The bundle copies modified `working/` files, `manifest.json`,
  `preflight.json`, `preflight.md`, and `diff.json` into a review directory and
  zip archive. It self-checks the created bundle and does not apply changes to
  source extension files or SQL;
- validate a review bundle independently: check manifest/preflight/diff
  consistency, copied modified-file hashes, required review files, and zip
  archive contents;
- create a disposable extension XML staging copy from a validated review bundle.
  This copies the source extension directory to staging, overlays bundle files,
  writes `_codex_staging_manifest.json`, and still does not modify source
  extension files or SQL. The creation command validates the staging copy before
  returning success;
- validate a disposable extension XML staging copy independently: check staging
  manifest schema, safety flags, staged file hashes, source extension freshness,
  and the recorded review bundle;
- validate runner configuration for disposable 1C validation: require
  `onec_extension_runner_config.v1`, reject production-like base references,
  reject secrets/credentials in the config file, and require
  `disposable_base_confirmed=true`;
- create a disposable-base validation plan for a staged extension. The plan is
  read-only: it does not launch 1C, does not modify SQL/source files, marks
  production bases as forbidden, lists required Designer/Enterprise checks, and
  records the evidence files a future runner must produce. If runner config is
  provided, it must pass the runner-config check;
- create pending manual evidence templates from a validation plan. This only
  creates files to be filled with disposable-base 1C logs and confirmations; it
  never marks validation as passed;
- check filled validation evidence: every expected evidence file must exist and
  no file may still contain the pending template markers. Evidence must state
  explicit `passed`, `success`, or `ok` status; `changed-objects-smoke.json`
  must have passed status for every changed object. Evidence files are also
  scanned for secret-like key/value text;
- aggregate final validation gates for human review: validation plan must be
  ready, staging must still pass, evidence must pass, safety flags must still
  forbid production/SQL/source writes. The result can only become
  `validated_for_human_review`; it never authorizes automatic production apply;
- render the final validation release check as a human-readable Markdown report
  that keeps the no-automatic-production-apply safety message visible;
- return file hashes, status, unified patches, line counts, and hunk counts;
- aggregate proposal safety, workspace integrity, source freshness, semantic
  validation, and diff gates into a single preflight status: `blocked`,
  `ready_for_editing`, or `ready_for_review`;
- provide the diff evidence needed before any extension packaging or human
  approval step.

## Object Metadata

Command:

```text
python scripts/get_1c_object_metadata.py
  --kind <ConfiguratorKind>
  --name <ConfiguratorName>
  --view effective|base|extension
  --extension <ExtensionName>
  --index <unified object route index>
```

Output schema:

```text
onec_object_metadata.v1
```

Purpose:

- return agent-facing metadata in 1C terms;
- default to `view=effective`, the current working metadata picture;
- support `view=base` for the main configuration only;
- support `view=extension --extension <name>` for one extension's additions
  and modifications;
- keep `origin.layer`, `origin.extension`, and `effective_action` in JSON while
  allowing human-facing answers to stay concise.

Example expectations for `Документ.ПриходнаяНакладная`:

```text
effective: 66 attributes, includes ДатаСоздания from ДоработкаРарус
base:      65 attributes, excludes ДатаСоздания
extension ДоработкаРарус:
  - ДатаСоздания: added
  - ДокументОснование: modified
```

## Read Object View

Command:

```text
python scripts/read_1c_object_view.py
  --kind <Kind>
  --name <Name>
  --view effective|base|extension
  --extension <ExtensionName>
  --summary <structured metadata DBNames summary>
  --validation <predicted column validation>
  --route-index <unified object route index>
  --output-dir <run output directory>
```

Unicode-safe shell variant:

```text
--name-b64 <utf8-base64-name>
```

Output schema:

```text
onec_sql_read_view.v1
```

Purpose:

- resolve the visible 1C object name first;
- locate enriched metadata by `kind/name`;
- if the object is absent from the prepared summary, build a metadata card from
  resolved XML and DBNames evidence;
- build SQL projection;
- execute SQL read after live SQL table-column validation;
- resolve simple references;
- resolve composite references;
- resolve composite value branches;
- attach enum presentations from metadata routes;
- return an agent-facing object view with raw evidence preserved.

Read views follow the same layer model as metadata:

```text
effective: current working data, including active extension companion tables
base:      base configuration tables only
extension: standard identity fields plus fields added/modified by one extension
```

Agent-facing metadata should present 1C types in Russian by default, matching
how programmers usually write/read 1C code: `СправочникСсылка.Номенклатура`,
`ДокументСсылка.ПриходнаяНакладная`, `ПеречислениеСсылка.ВидОперации`,
`Строка`, `Число`, `Булево`, `Дата`. Canonical XML/storage types such as
`cfg:CatalogRef.*` and `xs:boolean` remain in `value_type.types` for internal
adapter logic and for explicit debug/storage views.

Object metadata is effective metadata, not base-only metadata. Active extension
overlays must be applied before returning attributes, tabular sections, forms,
modules, or code context. Extension-added attributes are returned alongside base
attributes with source metadata; extension-adopted objects keep override
evidence in `extension_overrides`. A base-only answer is a debug/storage view,
not the default agent-facing answer.

For SQL reads, active extensions can move effective data into companion tables
with `Xn` suffixes, for example `_Document675X1` and
`_Document675_VT8000X1`. The read executor must choose the effective live table
by schema coverage and row availability, and record that decision in
`diagnostics.effective_tables`. Agents should see only the metadata object and
fields; companion table names remain storage diagnostics.

`onec_sql_read_result.v1` may include `diagnostics.pruned_missing_columns`.
These are columns present in the metadata projection but absent from the live
SQL table at execution time. The executor removes only those confirmed-missing
columns before running `SELECT`.

## Enum Presentation Map

Command:

```text
python scripts/build_1c_enum_presentation_map.py
  --index <unified object route index>
  --output <json>
```

Output schema:

```text
onec_enum_presentation_map.v1
```

Purpose:

- map SQL enum `_EnumOrder` values to metadata enum value `uuid/name/synonym`;
- use exported XML route evidence as the source of enum value order;
- prefer base configuration enum XML over extension XML when the same enum name
  exists in both, while keeping extension evidence available in the route index.

This command is an offline analysis aid. Runtime `data.list`/`data.get` enum
presentation is decoded from SQL metadata and does not require an XML export.

## Object Artifacts

Command:

```text
python scripts/get_1c_object_artifacts.py
  --index <unified object route index>
  --kind <Kind>
  --name <Name>
  --output <json>
```

Output schema:

```text
onec_object_artifacts.v1
```

Purpose:

- return owner object routes;
- list related XML top objects under the owner path;
- list forms, templates, object modules, form modules, and command modules
  from the exported XML filesystem evidence.

This is currently XML/filesystem backed. The SQL/CAS-backed implementation must
preserve the same output shape.

## Object Code Context

Command:

```text
python scripts/get_1c_object_code_context.py
  --index <unified object route index>
  --kind <Kind>
  --name <Name>
  --view effective|base|extension
  --extension <ExtensionName>
  --output <json>
```

Output schema:

```text
onec_object_code_context.v1
```

Purpose:

- return the module map for an object without loading large BSL contents;
- keep module names in 1C terms such as `МодульОбъекта`,
  `МодульМенеджера`, `МодульФормы.ФормаДокумента`, and
  `МодульКоманды.<ИмяКоманды>`;
- for `view=effective`, return base modules with active extension overlays
  attached under `extension_overlays`;
- for `view=base`, return only main configuration modules;
- for `view=extension --extension <name>`, return only modules from the
  selected extension.

## Module Content

Command:

```text
python scripts/get_1c_module.py
  --index <unified object route index>
  --kind <Kind>
  --name <Name>
  --module <ModuleName>
  --view effective|base|extension
  --extension <ExtensionName>
  --routine <ProcedureOrFunctionName>
  --max-chars <N>
  --output <json>
```

Output schema:

```text
onec_module_content.v1
```

Purpose:

- read the requested BSL module or one routine from that module;
- for `view=effective`, include the base module and matching active extension
  overlays, preserving their order and origin evidence;
- keep full file paths, relative paths, sizes, truncation markers, and line
  starts so a later write path can target the exact source.

## Form Context

Command:

```text
python scripts/get_1c_form_context.py
  --index <unified object route index>
  --kind <Kind>
  --name <Name>
  --form <FormName>
  --view effective|base|extension
  --extension <ExtensionName>
  --max-items <N>
  --output <json>
```

Output schema:

```text
onec_form_context.v1
```

Purpose:

- return common forms by configurator-visible common form name, for example
  `ОбщаяФорма.t_Форма` / `CommonForm.t_Форма`;
- return object-owned forms by configurator-visible object and form names, for
  example forms under catalogs, documents, data processors, reports, registers,
  charts, exchange plans, business processes, tasks, and other form-capable
  metadata objects;
- parse `Ext/Form.xml` into agent-facing form events, items, attributes, and
  commands without requiring the agent to know XML paths;
- return both raw item `title` and computed `effective_title` with
  `title_resolution`, because a form item can inherit its visible title from a
  bound form command, form attribute, or value-table column when its own title
  is empty;
- expose `title_resolution.edit_targets.default_change_target` and
  `one_off_change_target`: default changes should edit the inherited source
  such as command/attribute title, while one-off form-only changes should edit
  the form item title;
- keep runtime form reads SQL-only; XML paths are accepted only by offline
  analysis scripts and are rejected by the running adapter;
- for `view=effective`, return the base form with active extension form
  overlays attached under `extension_overlays`;
- for `view=base`, return only the main configuration form;
- for `view=extension --extension <name>`, return only the selected
  extension's form structure.

Selector rules:

- `CommonForm` / `ОбщаяФорма` is a top-level metadata object kind, not a child
  form of an owner object;
- `Form` is reserved for child form payloads owned by a metadata object;
- when a task says "форма <name>" without an owner, search common forms first,
  then ask for or infer the owner object before falling back to global scans;
- for live SQL analysis, build `metadata.form.owner_index.build` before relying
  on name-only form selectors. The index resolves `CommonForm.<name>` or
  object-owned form routes through extension manifests/DBNames and the actual
  ConfigCAS `.0` form payload, storing `module_ref` and `bsl_offset` for form
  module reads;
- form structural edits such as adding a form command, adding a command bar
  button, or inserting a new form item are Form.xml node operations. They must
  not be routed through scalar saved-state property writers that only change an
  existing decoded target;
- `metadata.form.command_button.write` handles the command/button case through
  SQL saved-state payloads: it clones existing command and command-button nodes
  from the same form, appends them with the byte-preserving `append_child`
  structural edit, increments declared section counts such as
  `{marker,count,record...}` when present, and uses the normal
  proposal/apply/rollback gates. XML may still be used to analyze or train the
  rule, but live apply remains SQL-only.
- After form command/button `apply` or `apply_and_verify`, the adapter returns
  `semantic_verify` with command, button, handler routine, command-handler
  link, button-command link, decoded command path, and decoded button path.
  Repeated upsert calls must be idempotent and return
  `idempotency.status=already_exists` instead of appending duplicate nodes.
- `metadata.form.command_button.verify` is the read-only equivalent for the
  same workflow. It resolves saved-state form targets from public selectors and
  returns the command/button/handler/link checks without preparing, proposing,
  or applying SQL changes.
- Public command/button write and verify responses use compact saved-state
  search evidence: `counts` plus `selected_form`, not the full candidate list.
- `metadata.form.write_target.verify` is a read-only preflight for existing form
  element/command/attribute writes. It reports `writable_now`, `needs_prepare`,
  a public `form_path` for the write node, and the `metadata.saved_state.prepare`
  payload when the save layer is missing.
- `metadata.write.history` lists recent adapter write operations, including
  `code.write`, or returns a specific `operation_id` with status, routed method,
  target summary, backup ids, and full result for a single operation. It can
  filter by `operation_method`, `status`, `routed_method`, or `backup_id`.
  Pass `include_summary=true` for aggregate counts by method, status, routed
  method, and operations with backups.
- `metadata.write.rollback` rolls back one saved-state backup selected by
  `operation_id` or `backup_id`. It is an explicit apply operation and requires
  `allow_sql_saved_state_rollback=true`.
- The deployment verification stack includes rollback safety reports:
  `write-rollback-safety-smoke.json` for REST and
  `write-rollback-safety-mcp-smoke.json` for MCP. These reports must show the
  method in `help.methods`, readable write history, and blocked rollback when
  `allow_sql_saved_state_rollback` is absent.
- `metadata.saved_state.prepare` prepares an object's saved-state working copy.
  Prefer a public selector (`ref` or `kind/name`) together with
  `layer=base_saved_state|extension_saved_state`. Its default response reports
  the selected 1C object, active/saved semantic layers, record counts, and
  readiness without exposing database/table names, storage files, hashes, or
  row details. `include_storage=true` retains the low-level diagnostic response.
  `plan` remains the default and performs no write; `apply` and
  `apply_and_verify` still require `allow_sql_saved_state_prepare=true`.
- `metadata.saved_state.ensure` is the preferred idempotent public facade for
  that operation. It resolves `ConfigSave` or `ConfigCASSave` from the public
  object/extension selector, copies only missing active parts, and never asks a
  user to create a missing save layer in Configurator. It remains a plan by
  default; its apply modes use the same explicit SQL gate.
- `metadata.saved_state.ensure.rollback` removes only rows inserted by the
  opaque receipt returned from an ensure apply. It SHA-checks every row first,
  requires `allow_sql_saved_state_rollback=true`, and never touches active
  `Config` or `ConfigCAS`.
- Every public RPC `next_resolution`/`next_call` entry uses `{method, params}`.
  `payload` is reserved for the outer RPC request envelope and internal
  apply-hint bodies; it must not be used as the arguments field of a public
  follow-up. Saved-state follow-ups use the semantic contract: `layer` plus a
  public object `ref` or `kind/name`. Internal `target_table`, `source_table`,
  `file_name`, `file_names`, `module_ref`, object GUIDs, and
  `include_storage` are not copied into public follow-up payloads. If only an
  opaque storage target is known, the follow-up reports `selector_required`
  instead of exposing that target.
- `metadata.saved_state.diff` is the agent-facing read-only comparison between
  a saved-state module and its active source. Prefer a public 1C selector such
  as `ref=Справочник.Номенклатура` or `kind/name` plus `module_ordinal`;
  the adapter resolves the saved-state table and physical file internally.
  Generated `module_ref` or `table` + `file_name` remain accepted for chained
  tooling. Name-first responses hide storage coordinates unless
  `include_storage=true`, and a missing save layer returns a name-first
  `metadata.saved_state.prepare` payload. The method maps
  `ConfigSave -> Config` and `ConfigCASSave -> ConfigCAS`, reads both payloads
  from live SQL, and returns `changed`/`unchanged`, `needs_prepare`, current
  hashes, compact text/tree diff, and
  `freshness.status=live_sql_verified`.
  Deployment verification persists REST/MCP smoke reports as
  `saved-state-diff-smoke.json` and `saved-state-diff-mcp-smoke.json`.
- `metadata.saved_state.status` is the read-only name-first overview for a whole
  save layer. Prefer `layer=base_saved_state|extension_saved_state`; the default
  response reports semantic aggregate state and object/record counts without
  exposing SQL tables, database names, files, hashes, or internal diff
  selectors. `include_storage=true` enables the diagnostic storage view, which
  accepts the backward-compatible `table=ConfigSave|ConfigCASSave`, compares
  saved rows with their active source by file part, size, and SHA1, and returns
  per-file `changed`, `unchanged`, or `saved_only` statuses.
- `configuration.activation.status` is the read-only activation boundary for
  `saved_state -> active`. It checks both layers by default, or accepts
  `layer=base_saved_state|extension_saved_state`, and delegates to the live SQL
  saved-state comparison above. It never uses the code cache or vector index as
  authority. `activation_required=true` means at least one `changed` or
  `saved_only` object exists. If the configured scan limit is reached without a
  detected difference, the result is `inconclusive` rather than a false
  `up_to_date`.
- `configuration.activation.plan` uses that live status to build a read-only
  handoff. When changes exist, it returns a `{method, params}` review call, a
  manual Designer action (`Обновить конфигурацию базы данных`, F7), and a live
  verification call whose expected status is `up_to_date`. The current adapter
  does not start Designer or mutate the active configuration, even when
  `ONEC_ADAPTER_ENABLE_EXTERNAL_1C` is enabled. A future execution method must
  use a dedicated Designer bridge and separate explicit confirmation.
- `configuration.activation.request` creates an expiring adapter-local request
  only when the live status is `activation_required`. The request fingerprint
  includes the exact pending storage files and their saved/active SHA1 values;
  storage coordinates and hashes are not returned as the public activation
  status. Requests and lifecycle events are stored outside the infobase in the
  local SQLite database selected by `ONEC_ADAPTER_STATE_DB`, in
  `configuration_activation_requests` and
  `configuration_activation_events`. The legacy JSON path selected by
  `ONEC_CONFIGURATION_ACTIVATION_STATE_FILE` is imported once and then remains
  read-only.
- `configuration.activation.execute` currently accepts only `mode=debug` and
  requires `confirm_activation=true` plus the exact request id. It repeats the
  live SQL read and rejects the request if it expired, the base differs, the
  pending state disappeared, or any fingerprinted file changed. A successful
  result is `debug_accepted`; Designer is not started and the active
  configuration is not changed. Use
  `configuration.activation.request.status` to inspect the adapter-local
  request state.
- `configuration.activation.request.cancel` requires `confirm_cancel=true` and
  records a lifecycle event for the exact request. Cancellation is idempotent
  and has no 1C side effect. `configuration.activation.audit` returns a bounded
  base-scoped list of request states and events without exposing the internal
  per-file fingerprint evidence.
- `configuration.activation.capabilities` reports only boolean/configuration
  readiness: runner kind, presence of a Designer path and infobase selector,
  external-1C enable flag, and supported workflow gates. It never returns the
  executable path, runner URL, infobase selector, users, passwords, or tokens.
  The base layer reports documented `/UpdateDBCfg` as `debug_only`; extension
  activation reports `manual_only` until a verified platform command is
  implemented.
- `configuration.activation.bridge.probe` performs an explicit debug-only
  readiness call. For a local runner it checks that the configured Designer
  executable exists and exactly one infobase selector is present. For an HTTP
  runner it calls `/configuration/activation/debug` using the existing runner
  token configuration. Both paths return only booleans and runner kind:
  Designer is not started, credential values are not read by the local probe,
  and paths, URLs, selectors, users, and secret names are not returned.
- `configuration.activation.execute` accepts optional `bridge_debug=true`.
  After all request, expiry, confirmation, live-SQL, and fingerprint checks
  pass, the adapter sends only `base_id`, semantic layer, request id, and the
  64-hex fingerprint to the debug runner. The runner must return a matching
  request/fingerprint plus a 64-hex receipt. A missing, mismatched, or
  not-ready receipt blocks acceptance. A valid receipt records the
  `bridge_debug_accepted` lifecycle event; it still does not start Designer or
  change the active configuration.
- `configuration.activation.verify` closes the manual activation loop for an
  exact request. It repeats the live SQL comparison with no cache. The result
  is `not_activated` when the original fingerprint is still pending,
  `changed_since_request` when pending files differ from the confirmed
  fingerprint, and `verified_up_to_date` only when saved and active layers
  align. SQL alignment does not prove that Designer performed activation—it
  can also result from discarding or replacing saved changes—so the response
  reports `activation_proven=false`.
- `metadata.saved_state.changes.list` is the read-only pending-change overview
  across the base and extension saved-state layers. Use the semantic
  `layer=base_saved_state|extension_saved_state` filter when only one layer is
  needed; the low-level `table` filter is retained for compatibility. Its
  default response is name-first: object/form/module context is resolved
  automatically, layer names are semantic, and SQL tables/files, GUIDs, hashes, module refs, concrete diff
  selectors, low-level actions, and SQL-specific freshness vocabulary are
  hidden. Unresolved changes remain in the
  counts and are marked `context_unresolved`. Use `include_storage=true` for
  the previous per-file diagnostic view with concrete `diff_selector` links.
  `include_unchanged=true` also includes unchanged entries. The freshness of
  the pending-change list remains live SQL verified, while context is
  explanatory metadata. Pass
  `group_by_context=true` to also return compact `groups` keyed by resolved
  form/module/object context; grouping uses the same best-effort context
  enrichment and falls back to per-file groups when context cannot be resolved.
  In diagnostic storage mode each group includes compact `selectors`: per-file
  `diff` selectors, plus
  `module_refs` and `write_plan_targets` when the resolved module context
  exposes them. Groups also include `next_actions`, derived from those
  selectors, for safe follow-up calls such as inspecting a diff, reading a
  module through `code.read state=working`, or running read-only
  `metadata.write.preflight`. `action_summary` counts those follow-up calls by
  kind/method, and `recommended_next_action` points to the first safe action,
  preferring diff inspection before module reads and write preflight checks.
  When `group_by_context=true`, the root response also includes an aggregated
  `action_summary` and root `recommended_next_action` with the selected group
  identity plus the action payload.
- Saved-state form module writes refresh the SQL-derived code index for the
  embedded module. Cache/vector results remain acceleration only; verified
  answers still require `payload_sha1` and `text_sha1` revalidation against
  current SQL.

## Code Index And Vector Candidates

SQL remains the source of truth. Code cache and vector search are acceleration
layers only.

Freshness statuses:

- `live_sql_verified`: code was read directly from SQL for the answer;
- `cache_hit_verified`: cache candidate was rechecked against current SQL
  `payload_sha1` and `text_sha1`;
- `cache_hit_stale`: cache candidate exists, but current SQL no longer matches;
- `cache_refreshed_from_sql`: a stale cache row was rebuilt from current SQL
  and will be reranked before it can be returned;
- `vector_candidate_unverified`: vector result is only a retrieval candidate.
- `dirty_with_live_fallback`: local coverage is incomplete, so the response
  includes a direct live-SQL search for newly added code.

RPC methods:

```json
{
  "method": "metadata.code_index.build",
  "payload": {
    "base_id": "<base-id>",
    "table": "ConfigCAS",
    "prefix": "<optional file prefix>",
    "max_items": 500,
    "include_vectors": true
  }
}
```

- `metadata.code_index.status`: reports cache/module/vector chunk counts,
  pending outbox events, overlay tombstones, and the local snapshot token;
- `metadata.code_index.search`: strict lexical search over cached BSL. Strict
  mode is the default: it overfetches, verifies against live SQL, refreshes and
  discards stale rows, and switches to live SQL while the index is dirty;
- `metadata.code_index.verify`: verifies one `module_ref` against live SQL;
- `metadata.code_index.refresh_changed`: verifies search candidates and refreshes
  stale modules from SQL;
- `metadata.code_index.sync_pending`: processes exact dirty/outbox targets after
  saved-state writes;
- `metadata.code_index.poll_changes`: detects Configurator/external changes.
  Saved tables are checked by default; use `include_active=true` for a less
  frequent active-configuration scan;
- `metadata.code_vector.pending`: returns current BSL chunks that still need an
  embedding for a requested model, with `chunk_id` and `text_sha1`
  preconditions. Optional `chunk_kinds=["routine"]` and `max_text_chars`
  support fast incremental passes without falsely marking skipped long chunks
  as embedded;
- `metadata.code_vector.embedding.upsert`: stores an external embedding only
  while those preconditions still match. Changed or removed chunks return
  `conflict`;
- `metadata.code_vector.search`: hybrid lexical/vector search over cached
  module/routine chunks. It overfetches, verifies live SQL, reranks after stale
  refreshes, suppresses active rows shadowed by saved state, and returns
  name-first `object_ref`/`object_selector` fields.

If older global index rows have empty owners, run
`metadata.module_owner_cache.backfill` page by page. Search also performs a
cheap local owner lookup for top candidates. For base `Config`/`ConfigSave`
modules whose part name starts with a GUID, the adapter resolves that GUID
against current metadata, requires an exact identity match, and persists the
verified public owner. It never launches a configuration-wide owner scan on
the hot search path.

For active extension `ConfigCAS` modules, the adapter follows the current
extension manifest from the exact CAS content hash to the owning descriptor,
decodes its public kind/name, and returns the extension name/GUID in both
`owner.extension` and `object_selector`. The manifest relation and descriptor
are cached locally only after this current-SQL resolution. Hashes are never
presented as object names.

`strict=true` and `verify=true` are the safe defaults. `strict=false` is a
diagnostic/candidate mode and must not drive programming changes.

The adapter keeps revision, dirty/outbox, tombstone, and vector data in the
local adapter SQLite database. It does not add project tables or settings to
the 1C SQL database.

External embeddings are optional. The worker supports deterministic local
hashing for tests and any OpenAI-compatible embedding endpoint:

```powershell
python scripts\embed_1c_code_vectors.py `
  --base-id upo_test `
  --embedding-provider openai-compatible `
  --embedding-model <code-embedding-model> `
  --embedding-base-url http://<embedding-host>:<port> `
  --json

python scripts\search_1c_code_vectors.py "где рассчитывается налог" `
  --base-id upo_test `
  --embedding-provider openai-compatible `
  --embedding-model <code-embedding-model> `
  --embedding-base-url http://<embedding-host>:<port> `
  --embed-pending `
  --json
```

API keys are read only from the configured environment variable and are not
written to SQLite, responses, or project files. The embedding model label and
vector dimensions are stored so vectors from different models cannot be mixed.
For Matryoshka-capable OpenAI-compatible models the code worker includes the
requested dimension in the cache label (for example `@d384`), slices a longer
response deterministically, and normalizes the stored vector again.

Operational modes:

- `fast`: strict cache search plus SQL hash verification;
- `live`: direct SQL search/read, slower but authoritative;
- `background_refresh`: intended for long cache warming jobs.

Never apply code changes from cache or vector output alone. Use the returned
`read_selector` after freshness is `cache_hit_verified`, then run the normal
live write preflight. A non-null snapshot token identifies a clean local index;
it is not a native 1C repository lock.

## Saved-State Form Search And Write Target Resolve

RPC methods:

```json
{
  "method": "metadata.saved_state.forms.search",
  "payload": {
    "base_id": "<base-id>",
    "tables": ["ConfigCASSave", "ConfigSave"],
    "element": "КомандаПример1",
    "scan_limit": 1000
  }
}
```

```json
{
  "method": "metadata.form.write_target.resolve",
  "payload": {
    "base_id": "<base-id>",
    "table": "ConfigCASSave",
    "form": "ТестНастройки",
    "command": "КомандаПример1",
    "property": "Заголовок",
    "value": "ПРОВЕРКА"
  }
}
```

```json
{
  "method": "metadata.form.write_target.verify",
  "payload": {
    "base_id": "<base-id>",
    "extension": "<extension-name>",
    "form": "ТестНастройки",
    "command": "КомандаПример1",
    "property": "Заголовок"
  }
}
```

Output schemas:

```text
onec_saved_state_form_search.v1
onec_form_write_target_resolution.v1
```

Purpose:

- search saved-state form payloads without running the heavyweight global
  definition scan;
- resolve an agent-facing write intent into `table`, `file_name`, decoded
  section (`items`, `commands`, `attributes`, `attribute_fields`, `tables`, or
  `command_bars`), exact path, current value, and writable properties;
- prefer selector intent when names overlap: `element`/`element_name` search
  form items first, then commands; `command` searches commands; `attribute`
  searches form attributes and value-table fields. Use `element_path`/`path`
  or `element_id`/`id` for strict physical disambiguation;
- return both the legacy `target`/`effective_target` fields and the
  source-aware `display`, `write_target`, and `alternatives` fields when the
  visible property is inherited. For example, an empty element caption may be
  resolved to the linked form command caption;
- use the form property registry for aliases and verification rules. Current
  registry covers `id`, `name`, `title`/`Заголовок`, `path_to_data`/
  `ПутьКДанным`, `visible`/`Видимость`, `enabled`/`Доступность`, and
  `read_only`/`ТолькоПросмотр`, plus form layout/button properties such as
  `Использование`, `Группа`, `Вид`, `Отображение`, `ПоложениеЗаголовка`,
  `ПоложениеВКоманднойПанели`, `УникальностьКоманды`, `ЦветФона`,
  `ЦветТекста`, and `ЦветРамки`;
- refuse to silently create a local title override when a caption is derived
  from `ПутьКДанным`. Pass `source=local_override` on the edit when a local
  form-element override is intentional;
- resolve `ПутьКДанным` captions by source:
  if the path points to a form attribute, write the form attribute title; if it
  points to a tabular form attribute field such as `ТЗ.К1`, write that field
  title; if it points to an object/configuration attribute such as
  `Объект.<Реквизит>`, write the local form element title instead;
- return candidate diagnostics when a selector is missing or ambiguous;
- produce a human-readable semantic diff such as
  `КомандаПример1.Заголовок: Пример1 -> ПРОВЕРКА`.

## Saved-State Form Write Matrix

RPC methods:

```json
{
  "method": "metadata.form.write_matrix.build",
  "payload": {
    "base_id": "<base-id>",
    "table": "ConfigCASSave",
    "file_name": "<form-file-name>"
  }
}
```

```json
{
  "method": "metadata.form.write_matrix.smoke",
  "payload": {
    "base_id": "<base-id>",
    "table": "ConfigCASSave",
    "file_name": "<form-file-name>",
    "allow_sql_saved_state_apply": true,
    "allow_sql_saved_state_rollback": true,
    "max_candidates": 10,
    "learning_id": "write-matrix-case"
  }
}
```

Purpose:

- enumerate all decoded writable scalar form properties that the adapter can
  read from saved-state form payloads;
- resolve each property through the same source-aware routing used by
  `metadata.write`, including command captions, form attribute captions, and
  tabular form attribute field captions;
- classify each entry as `can_smoke=true` or return a reason such as
  `identity_or_binding_property`, `enum_values_unknown`, or
  `empty_local_string_requires_codec_probe`;
- run `can_smoke` entries through `apply_and_rollback` and record verified
  routes in a write-learning report when `learning_id` is provided.

## Routine Override Resolution

RPC method:

```text
metadata.resolve_overrides
```

Purpose:

- build a routine chain for a concrete object and method name across base and
  extension modules;
- include modules of forms owned by the selected object, including extension
  objects resolved from a public `ref`/`kind`/`name` without requiring an
  extension GUID or CAS key;
- apply the same source policy as code reads: `state=working` prefers the saved
  form-module counterpart with active fallback, `state=save` is saved-only,
  `state=active` is active-only, and `state=both` can report both versions;
- mark every chain link with `activation_state=active|saved_state`, so a saved
  routine is never mistaken for already applied runtime code;
- return public read selectors for every found routine without exposing storage
  ids by default;
- expose `chain[].extension_action` for each routine link. For base
  configuration links this is `operation_class=base_definition`;
- for extension links, normalize known action evidence into
  `insert_before`, `insert_after`, `replace`, or `replace_with_control`;
- classify an unannotated routine of an object defined by the extension itself
  as `extension_definition`; it is not an unresolved interception of a base
  routine and therefore does not trigger the unknown-action write guard;
- when a routine is found in an extension but the action metadata is not yet
  resolved, return `extension_action.status=unknown` and
  `operation_class=unknown_extension_action`. The agent must not treat this as
  an ordinary replace;
- for `replace_with_control`, carry `requires_control_fragment=true`; write
  planning still requires the controlled base fragment, `controlled_fragment`,
  or `expected_old_contains` before apply.
- return `write_plan_evidence`, a ready fragment for `metadata.write.plan`
  containing `target.kind=module`, `routine_name`, object selector fields, and
  `target.extension_action` when available;
- include `write_plan_evidence.next_resolution.method` and
  `write_plan_evidence.next_resolution.params` for
  `metadata.saved_state.modules.search`, so the agent has the next safe lookup
  for the module. The follow-up uses `ref` or `kind/name` and the semantic
  `layer=base_saved_state|extension_saved_state`; it does not expose
  `owner_guid` or SQL table names. The saved-state module search also accepts
  the object selector aliases and resolves names to storage identities
  internally. With the explicit diagnostic
  `include_storage=true` option, its matching streams return
  `write_plan_target` with `module_ref`, `file_name`, `stream_index`, and
  `expected_sha1` for the concrete `metadata.write.plan` target. This override
  evidence itself is not a concrete saved-state write route.
- callers can compose `write_plan_evidence.target` with a returned
  `streams[].write_plan_target`, add the required operation guards, and pass the
  result to `metadata.write.plan`; the accepted plan must route to
  `metadata.module.write_apply` without inventing storage field names.

This method is read-only evidence for planning. It tells the agent what must be
known before calling `metadata.write.plan`; it does not select a concrete saved
state route by itself.

## Metadata Write

RPC method:

```json
{
  "method": "metadata.write",
  "payload": {
    "base_id": "<base-id>",
    "target": {
      "kind": "form",
      "table": "ConfigCASSave",
      "form": "ТестНастройки",
      "element": "КомандаПример1"
    },
    "mode": "plan",
    "edits": [
      {"property": "Заголовок", "value": "ПРОВЕРКА"}
    ]
  }
}
```

Output schema:

```text
onec_metadata_write.v1
```

Standard object identity properties use the same high-level method and public
1C selector:

```json
{
  "method": "metadata.write",
  "payload": {
    "base_id": "<base-id>",
    "target": {
      "area": "object",
      "kind": "Catalog",
      "name": "Номенклатура",
      "property": "synonym"
    },
    "value": "Номенклатура товаров",
    "expected_old": "Товары",
    "mode": "plan"
  }
}
```

This route delegates to `metadata.object.property.write`. It resolves the
object GUID and exact saved-state tree path internally, supports only existing
`synonym` locales and scalar `comment`, and preserves the original payload
format. Apply modes retain SHA1/semantic preconditions, backup, readback
verification, and rollback. Object rename and structural collection edits are
not part of this route.

An existing child metadata identity uses the same method:

```json
{
  "method": "metadata.object.property.write",
  "payload": {
    "base_id": "<base-id>",
    "member_ref": "Catalog.Номенклатура.Attribute.Артикул",
    "property": "comment",
    "value": "Код товара поставщика",
    "allow_saved_state_write": true,
    "execution_mode": "plan"
  }
}
```

`member_kind` + `member_name` is also accepted. If the same child name occurs
in several collections, callers must provide the full `member_ref`; the
adapter returns `ambiguous` instead of choosing a GUID. Supported existing
member categories are `Attribute`, `TabularSection`, `Dimension`, and
`Resource`, including Russian aliases.

Adding one requisite uses a separate clone-based method:

```json
{
  "method": "metadata.object.member.add",
  "payload": {
    "base_id": "<base-id>",
    "template_member_ref": "Catalog.Номенклатура.Attribute.Артикул",
    "new_member_name": "КодПоставщика",
    "new_member_synonym": "Код поставщика",
    "allow_saved_state_write": true,
    "execution_mode": "plan"
  }
}
```

The adapter requires an existing `Attribute` template from the same collection,
generates a deterministic GUID internally, rejects duplicate names, clones the
template type/settings, and appends the cloned record to the same declared
collection. The proposal verifies both the appended node and updated declared
count. Apply/verify/rollback use the normal saved-state gates and backup.

For a tabular-section column, use the complete nested template path:

```text
Document.АвансовыйОтчет.TabularSection.Запасы.Attribute.Номенклатура
```

Name uniqueness and the deterministic GUID seed are scoped to that tabular
section, so identical column names in other table parts do not conflict.

Scheduled-job schedule planning uses the same method and a public 1C reference:

```json
{
  "method": "metadata.write",
  "payload": {
    "base_id": "<base-id>",
    "target": {
      "kind": "schedule",
      "ref": "РегламентныеЗадания.ОбменДанными"
    },
    "schedule": {
      "begin_time": "09:00:00",
      "week_days": [1, 2, 3, 4, 5]
    },
    "allow_saved_state_write": true,
    "mode": "plan"
  }
}
```

This route returns `onec_scheduled_job_schedule_write.v1`, resolves the object
name to its GUID internally, and targets only `<guid>.0` in `ConfigSave`.

Purpose:

- provide one agent-facing write entry point;
- reject write requests that do not resolve to a full 1C path, explicit layer,
  or concrete saved-state/module reference;
- use the same planning shape as `metadata.write.plan` before any apply mode;
- return a blocked `metadata.write.plan` result instead of applying when the
  target is only an effective `canonical_path` without origin/layer evidence or
  a concrete saved-state/module reference;
- include the blocked plan's `apply_payload_hint` and `next_resolution` in the
  `metadata.write` response when a full form/module path can be parsed, so the
  agent can resolve the saved-state target without treating the effective view
  as writable;
- after the agent provides a concrete saved-state `file_name` or `module_ref`
  for a parsed full path, merge missing selector fields from the same
  `apply_payload_hint` into the apply payload, for example `form`/`element` or
  `routine_name`, while preserving explicit user payload values;
- preserve the concrete reference field type in write planning and apply hints:
  `module_ref`, `module_id`, `file_name`, and `form_guid` must not be collapsed
  into a generic string or rewritten as another selector field;
- reject incompatible concrete reference fields for the selected target kind
  with `concrete_reference_kind_mismatch`, for example `form_guid` on
  `target_kind=module` or `module_ref` on `target_kind=form`;
- do not call lower-level apply methods when the same read-only
  `metadata.write.plan` reports `allowed=false`; return `status=blocked`,
  `error=write_plan_blocked`, the blocking `problems`, and the plan instead;
- require `metadata.form.element.write_apply` and
  `metadata.module.write_apply` to run the same read-only plan gate before SQL
  apply, even when they are called directly instead of through
  `metadata.write`;
- v1 routes `target.kind=form` to
  `metadata.form.element.write_apply`;
- v1 routes `target.area=object` to
  `metadata.object.property.write` for `synonym` and `comment`;
- v1 routes `target.area=object` with `operation=add_attribute` to
  `metadata.object.member.add`;
- v1 routes `target.kind=schedule` to the named scheduled-job schedule writer;
  scalar schedule fields and structural `week_days`/`months` resize are
  supported through a verified full schedule-tree replacement;
- v1 routes saved-state form payload container module refs without `#stream`
  to the embedded form payload writer when `routine_name` and `routine_text`
  identify a single routine edit; this path preserves existing leading BSL
  directives such as `&НаКлиенте` if the replacement text omits them;
- treat saved-state preparation as an adapter concern: when `metadata.write`
  receives a current active form/module target from `Config` or `ConfigCAS`,
  `plan` returns a `metadata.saved_state.prepare` plan, while apply modes with
  `allow_sql_saved_state_apply=true` prepare `ConfigSave`/`ConfigCASSave`
  internally and continue against that save layer;
- preserve the same gates as the lower-level form runner:
  `plan`, `apply`, `apply_and_rollback`, explicit SQL apply/rollback flags,
  sha1 preconditions, backup, and semantic verification;
- keep remaining writers behind explicit target routing instead of ad-hoc
  direct SQL methods.

## Metadata Write Preflight

RPC method:

```json
{
  "method": "metadata.write.preflight",
  "payload": {
    "base_id": "<base-id>",
    "target": {
      "kind": "module",
      "module_ref": "ConfigCASSave:<file-name>#stream:0"
    },
    "intent": {
      "operation": "replace_with_control",
      "control_fragment": "<current-fragment>",
      "new": "<new-fragment>"
    }
  }
}
```

Output schema:

```text
onec_metadata_write_preflight.v1
```

Purpose:

- provide a read-only check immediately before `metadata.write`;
- combine `metadata.write.plan` with live SQL saved-state verification;
- return `status=ready`, `needs_prepare`, `needs_resolution`, or `blocked`;
- report `saved_state.freshness.status=live_sql_verified` for concrete
  saved-state targets verified against SQL;
- never create or modify `ConfigSave`/`ConfigCASSave`;
- let `metadata.write` keep saved-state preparation as an adapter concern:
  agents should not ask the user whether to create save rows.

## Metadata Write Plan

RPC method:

```json
{
  "method": "metadata.write.plan",
  "payload": {
    "base_id": "<base-id>",
    "target": {
      "canonical_path": "Справочник.Контрагенты.Наименование"
    },
    "intent": {
      "operation": "property_change",
      "property": "Синоним",
      "value": "Контрагент"
    },
    "preferred_layer": "auto"
  }
}
```

Output schema:

```text
onec_metadata_write_plan.v1
```

Purpose:

- resolve the requested change target to a full 1C path or return ambiguity
  candidates;
- infer `target_kind` from full path sections when it is not explicit, for
  example `...Форма.<FormName>.<Member>` routes as `form` and
  `ОбщийМодуль.<Name>.<Routine>` routes as `module`;
- read effective and origin evidence before selecting a write route;
- use `metadata.definition.find` for read-only origin lookup when a
  `canonical_path` is provided, returning compact matches, `origin`,
  `read_selector`, and `related_selectors` under `origin_lookup`;
- accept public `target.origin` or top-level `origin` from prior
  `code.search`, `code.read`, `modules.search`, or `modules.read` results and
  normalize it into `origin_lookup.method=provided_origin_evidence` for layer
  recommendation without repeating the lookup;
- accept public `target.extension_action`, top-level `extension_action`, or a
  single-item `extension_actions` list from `metadata.resolve_overrides`. If
  the write intent has no explicit operation, known action evidence may infer
  `route.operation_class`; unknown action evidence blocks planning with
  `extension_action_unknown`;
- block multi-item `extension_actions` with `extension_action_ambiguous` until
  the caller narrows the extension/module/routine context to one concrete
  action;
- reject mismatches between explicit module-code operation and extension action
  evidence with `extension_action_operation_mismatch`, because extension code
  changes must preserve `insert_before`, `insert_after`, `replace`, or
  `replace_with_control` semantics;
- return `ambiguous_origin_matches` when origin lookup finds more than one
  matching definition, even if all matches are in the same layer; callers must
  narrow object/form/module/routine context before apply;
- classify the current owner as base configuration, extension, saved-state
  working copy, generated extension source, or read-only reference evidence;
- return `route.recommended_write.write_surface` from origin evidence:
  `base_saved_state` for configuration origin, `extension_saved_state` for a
  resolved extension origin, `saved_state` for an already concrete saved-state
  origin, `blocked_unknown` for unresolved owners, or `blocked_conflict` when
  multiple layers match the target;
- normalize `preferred_layer` (`auto`, `base`, `extension`,
  `generated_extension_source`, including Russian aliases) and return
  `preferred_layer_conflict` when it disagrees with origin-derived
  `recommended_write`;
- accept `preferred_extension` as an optional extension name/GUID and return
  `preferred_extension_conflict` when it disagrees with the resolved extension
  owner;
- select the smallest allowed operation such as `add`, `property_change`,
  `insert_before`, `insert_after`, `replace`, `replace_with_control`,
  `append_routine`, `upsert_routine`, or `move_form_item`;
- normalize natural operation names into `route.operation_class`, including
  Russian phrases such as `вставить до`, `вставить после`, `вместо`, and
  `вместо с контролем`;
- return `route.apply_payload_hint` for concrete saved-state module/form
  routes, mapping normalized operations and guard fields into the next safe
  planner/apply method payload such as `metadata.module.write_apply`;
- include `target.concrete_reference_field` and
  `target.concrete_reference_source` when a concrete selector was provided, so
  the agent can distinguish `module_ref`, module `file_name`, form `file_name`,
  and `form_guid`;
- include selector fields derived from `canonical_path` in
  `route.apply_payload_hint`, for example object `kind`/`name`, `form`,
  `element`, and `routine_name`;
- mark `route.apply_payload_hint.ready_for_apply_method=true` only when the
  hint already has a concrete saved-state/module reference; selector-derived
  hints must use `ready_for_apply_method=false` and include `next_resolution`,
  for example `metadata.saved_state.modules.search` for modules or
  `metadata.form.write_target.resolve` for forms;
- report required guards: current sha1, expected old text, controlled fragment,
  extension order, conflict scan, BSL validation, and semantic readback;
- for module operations, block `replace_with_control` unless
  `control_fragment`, `controlled_fragment`, or `expected_old_contains` is
  present. Plain `old` is still useful replacement evidence, but it is not a
  controlled base fragment;
- when optional current-source evidence such as `current_text` or `source_text`
  is provided for `replace_with_control`, block stale plans with
  `control_fragment_drift` if the controlled fragment no longer matches that
  source;
- for module `replace`/`replace_with_control`, require old-code evidence
  (`old`, `control_fragment`, `expected_old_contains`, `expected_old_sha1`,
  `expected_contains`, or `expected_sha1`) and new code (`new`, `text`, or
  `routine_text`);
- for module `insert_before`/`insert_after`, require an anchor through
  `anchor`, `before`, `after`, or `expected_contains`;
- return `allowed=false` when the target is effective-only, ambiguous,
  active-applied, or missing required extension/control evidence.

The planner is read-only. Apply methods may consume the accepted plan, but must
still enforce the explicit saved-state or extension-source gates.

## Metadata Write Learning

RPC methods:

```text
metadata.write_learning.capture_before
metadata.write_learning.capture_after
metadata.write_learning.diff
metadata.write_learning.infer_rule
```

Purpose:

- support the manual learning loop: capture a saved-state form before a
  Designer edit, capture it after the edit, compare decoded writable
  properties, and infer a repeatable `metadata.write` payload;
- store learning artifacts under `ONEC_ADAPTER_WRITE_LEARNING_DIR` or
  `/data/adapter-write-learning`;
- store decoded targets, writable properties, storage sha1 and byte counts;
  raw payload hex is not stored in learning captures;
- keep learning scoped to saved-state form payloads in `ConfigSave` and
  `ConfigCASSave`.

Example:

```json
{
  "method": "metadata.write_learning.capture_before",
  "payload": {
    "base_id": "<base-id>",
    "learning_id": "case-command-title",
    "table": "ConfigCASSave",
    "form": "ТестНастройки",
    "command": "КомандаПример1"
  }
}
```

After a manual Designer edit:

```json
{"method": "metadata.write_learning.capture_after", "payload": {"base_id": "<base-id>", "learning_id": "case-command-title", "table": "ConfigCASSave", "form": "ТестНастройки", "command": "КомандаПример1"}}
```

Then:

```json
{"method": "metadata.write_learning.diff", "payload": {"learning_id": "case-command-title"}}
{"method": "metadata.write_learning.infer_rule", "payload": {"learning_id": "case-command-title"}}
```

## Saved-State Form Element Write Planner

RPC method:

```json
{
  "method": "metadata.form.element.write",
  "payload": {
    "base_id": "<base-id>",
    "table": "ConfigSave",
    "form_guid": "<form-guid>",
    "element": "<form-element-name>",
    "allow_saved_state_write": true,
    "include_payload": true,
    "edits": [
      {"property": "title", "value": "Новый заголовок"},
      {"property": "Видимость", "value": true},
      {"property": "Заголовок", "value": "Локальный заголовок", "source": "local_override"}
    ]
  }
}
```

Output schema:

```text
onec_change_proposal.v1
```

Purpose:

- resolve an agent-facing decoded form element (`element`, `element_id`, or
  diagnostic `element_path`) into exact form payload paths;
- build a `changes.propose` review payload for saved-state tables only:
  `ConfigSave` or `ConfigCASSave`;
- build form proposals in preserve-format mode: only the selected scalar token
  span is patched in the original brace text;
- redirect empty inherited captions to their real display source when known,
  such as a linked form command title;
- block non-explicit local caption overrides when the display caption appears
  to come from `ПутьКДанным`;
- auto-resolve `file_name` through `metadata.form.write_target.resolve` when a
  direct form file selector is not provided;
- include `semantic_diff` and candidate diagnostics for agent review;
- verify semantic readback through the same property registry aliases, so an
  edit like `command_bar_location` is checked against
  `ПоложениеВКоманднойПанели`;
- require explicit `allow_saved_state_write=true` so write intent is visible in
  logs and prompts;
- keep direct SQL updates blocked: `write_mode.sql_write_performed=false`.

## Saved-State Form Element Write Runner

RPC method:

```json
{
  "method": "metadata.form.element.write_apply",
  "payload": {
    "base_id": "<base-id>",
    "execution_mode": "apply_and_rollback",
    "table": "ConfigSave",
    "form_guid": "<form-guid>",
    "element": "<form-element-name>",
    "allow_sql_saved_state_apply": true,
    "allow_sql_saved_state_rollback": true,
    "edits": [
      {"property": "title", "value": "Новый заголовок"}
    ]
  }
}
```

Output schema:

```text
onec_form_element_write_apply.v1
```

Purpose:

- orchestrate the planner, saved-state apply, semantic verification, and
  optional rollback in one adapter call;
- support `execution_mode=plan`, `apply`, `apply_and_verify`, and
  `apply_and_rollback`;
- force `include_payload=true` internally for apply modes;
- require explicit SQL apply and rollback gates before touching saved-state
  rows.

## Saved-State Apply

RPC method:

```json
{
  "method": "storage.saved_state.apply_proposal",
  "payload": {
    "base_id": "<base-id>",
    "allow_sql_saved_state_apply": true,
    "proposal": "<full proposal from metadata.form.element.write or changes.propose>"
  }
}
```

Output schema:

```text
onec_storage_saved_state_apply.v1
```

Purpose:

- apply a reviewed proposal to `ConfigSave` or `ConfigCASSave`;
- require `proposal.encoded.payload_hex`, so the proposal must be created with
  `include_payload=true`;
- verify `proposal.original.sha1` against the current saved-state payload before
  writing;
- write backup evidence with rollback payload under
  `ONEC_ADAPTER_BACKUP_DIR` or `/data/adapter-apply-backups`;
- run the SQL update inside a transaction and verify readback sha1 after commit;
- when the proposal comes from `metadata.form.element.write`, re-read the form
  and verify the edited element property through `metadata.form.decode`;
- v1 only updates payloads stored as one SQL part. Multi-part payloads return
  `unsupported_part_layout` until a schema-aware multi-part writer is added.

## Saved-State Rollback

RPC method:

```json
{
  "method": "storage.saved_state.rollback",
  "payload": {
    "base_id": "<base-id>",
    "allow_sql_saved_state_rollback": true,
    "backup_id": "<backup-id-from-apply>"
  }
}
```

Output schema:

```text
onec_storage_saved_state_rollback.v1
```

Purpose:

- load backup evidence by `backup_id` or `backup_path`;
- apply the embedded rollback proposal through the same saved-state apply gate;
- return the nested apply result, including readback and semantic verification
  when available.

## Saved-State Backups List

RPC method:

```json
{
  "method": "storage.saved_state.backups.list",
  "payload": {
    "base_id": "<base-id>",
    "table": "ConfigCASSave",
    "file_name": "<saved-state-form-file>",
    "limit": 10,
    "diagnostic": true
  }
}
```

Output schema:

```text
onec_saved_state_backups.v1
```

Purpose:

- list local apply backups from `ONEC_ADAPTER_BACKUP_DIR` or
  `/data/adapter-apply-backups`;
- filter by base, table, and file name;
- return backup ids, source metadata, sha1 and byte counts without returning
  rollback payload hex.

## Saved-State Backup Retention

RPC method:

```json
{
  "method": "storage.saved_state.backups.prune",
  "payload": {
    "base_id": "<base-id>",
    "table": "ConfigCASSave",
    "older_than_days": 30,
    "keep_latest": 20,
    "limit": 500,
    "dry_run": true
  }
}
```

The method only inspects adapter-local backup JSON files. It is base-scoped,
defaults to `dry_run=true`, preserves `keep_latest` matching backups, and never
writes the 1C database. Actual deletion additionally requires
`dry_run=false` and `confirm_delete=true`. Backups referenced by
`metadata.write.history` are never selected for deletion. If history lookup is
unavailable for a base, its matching backup files remain protected.

## Kind Smoke

Command:

```text
python scripts/smoke_1c_read_view_kinds.py
  --kind InformationRegister
  --kind AccumulationRegister
  --kind AccountingRegister
  ...
```

Output schema:

```text
onec_read_view_kind_smoke.v1
```

Purpose:

- select first routed object of each kind from the enriched metadata summary;
- run the full read object view pipeline;
- record success/failure and command tails for regression tracking.

## Current Implemented Schemas

```text
onec_sql_read_projection.v1
onec_sql_read_result.v1
onec_sql_reference_resolution.v1
onec_sql_composite_reference_resolution.v1
onec_sql_composite_value_resolution.v1
onec_bsl_symbol_resolution.v1
onec_enum_presentation_map.v1
onec_object_resolution.v1
onec_object_brief_context.v1
onec_object_context_search.v1
onec_saved_state_object_comparison.v1
onec_saved_state_object_detail.v1
onec_saved_state_object_report.v1
onec_saved_state_object_report_check.v1
onec_saved_state_watch_once.v1
onec_saved_state_watch_once_check.v1
onec_saved_state_watch_run_list.v1
onec_saved_state_watch_run_list_check.v1
onec_saved_state_latest_watch_run.v1
onec_saved_state_latest_watch_run_check.v1
onec_saved_state_object_report_delta.v1
onec_saved_state_object_report_delta_check.v1
onec_saved_state_object_change_list.v1
onec_saved_state_object_change.v1
onec_task_context_plan.v1
onec_task_evidence_bundle.v1
onec_task_change_proposal.v1
onec_change_proposal_safety_check.v1
onec_patch_workspace_creation.v1
onec_patch_workspace_manifest.v1
onec_patch_workspace_integrity.v1
onec_patch_source_freshness.v1
onec_patch_workspace_semantic_validation.v1
onec_bsl_routine_edit.v1
onec_form_command_edit.v1
onec_form_button_edit.v1
onec_form_button_workflow.v1
onec_patch_workspace_diff.v1
onec_patch_bundle.v1
onec_patch_bundle_creation.v1
onec_patch_bundle_check.v1
onec_extension_staging.v1
onec_extension_staging_creation.v1
onec_extension_staging_check.v1
onec_extension_runner_config_check.v1
onec_extension_validation_plan.v1
onec_extension_validation_evidence_manifest.v1
onec_extension_validation_evidence_check.v1
onec_extension_validation_release_check.v1
onec_patch_preflight.v1
onec_structured_metadata_from_resolved_xml.v1
onec_sql_read_view.v1
onec_object_artifacts.v1
onec_object_code_context.v1
onec_module_content.v1
onec_form_context.v1
onec_metadata_write_plan.v1
onec_read_view_kind_smoke.v1
```

## Extension Evidence

Declared metadata targets and physical SQL hits must remain separate.

Example:

```text
declared target: cfg:CatalogRef.Пользователи -> _Reference348
physical hit:    _Reference348X1
```

The adapter may select an alternate presentation for convenience, but it must
keep `declared target`, `alternate_hits`, and selected presentation as separate
fields.
