# 1C Connector

Standalone-ready service for safe interaction with live 1C databases.

The connector is read-first and optimized for an operational coding loop where full XML export and EDT sync are too slow for every task.

Preferred live architecture:

- SQL-only connector for diagnostics, metadata decoding, and controlled
  saved-state work in an explicitly authorised test base;
- a human-operated Configurator for viewing and applying pending changes;
- cached metadata/module snapshots with freshness checks;
- change proposals as reviewable artifacts, not direct production writes.

The connector is responsible for:

- metadata reads;
- BSL module search/read;
- read-only query validation and execution;
- metadata/module snapshots;
- change proposals and, only where a reverse codec is activation-proven,
  controlled `ConfigSave`/`ConfigCASSave` writes with rollback evidence.

The adapter never writes `Config`, `ConfigCAS`, or application data directly.
It does not automate Configurator and must not invent unknown 1C structures.
The protocol evidence base is [docs/1c-sql-protocol](../../../docs/1c-sql-protocol/README.md).

Contracts:

- `contracts/openapi.yaml`
- `policies/read-only-query.yaml`
- `policies/change-workflow.yaml`
- `policies/config-layer-write-policy.yaml`
- `policies/sql-base-access-policy.yaml`
- `policies/xml-decoding-reference-policy.yaml`

The model must use this connector instead of inventing metadata or directly changing a live database.

XML exports are development-time evidence only. They may be analyzed by
repository scripts to infer and test generic SQL payload decoders, but the
running connector is configured only with a SQL entry for `base_id`. It does
not mount or read XML and rejects XML path arguments in runtime requests.

## Standalone boundary

This directory is the service boundary for the adapter. It is still developed
inside the current monorepo, but it should be kept movable as an independent
project.

Service-owned files:

- `adapter_1c_server.py`
- `contracts/openapi.yaml`
- `policies/*.yaml`
- `Dockerfile`
- `docker-compose.yml`
- `.env.example`
- `pyproject.toml`
- `service.yaml`
- sibling package `../parser`

Repository-owned integration files:

- `plugins/1c/mcp/adapter_1c_mcp.py`
- `plugins/1c/agent/`
- `plugins/1c/rag/`
- `plugins/1c/training/`
- top-level health and contract scripts under `scripts/`

The connector must not depend on RAG, training, or agent code. MCP and agent
code may depend on the connector contract.

## Local Run

From `plugins/1c`:

```powershell
python connector/adapter_1c_server.py
```

From `plugins/1c/connector` after installing package dependencies:

```powershell
python adapter_1c_server.py
```

Health without a concrete base:

```powershell
Invoke-RestMethod http://localhost:8011/health
```

Live database calls require `base_id` and SQL connection configuration.

## Configuration repository operations

Repository access is configured per `base_id`, preferably as a `repository`
object inside the same JSON entry used by `ONEC_SQL_BASES_JSON_FILE`. The
repository backend is never inferred from a bridge name or endpoint. Set
`backend` explicitly to `direct` or `karman_bridge`; both backends execute the
standard 1C Designer repository commands, while a Karman/Filebox bridge only
relays the native opaque TCP stream.

See `config/1c_repository_bases.example.json` for a secret-free example.
Passwords are resolved only from the configured environment-variable names.
The adapter does not return them or store them in lock-session state.

When the adapter runs in a Linux container and Designer is installed on the
Windows Docker host, use `runner.kind=http`. Run
`scripts/run_1c_repository_runner.py` on Windows with its own external base
configuration (example: `config/1c_repository_runner_bases.example.json`). The
container sends only `base_id`, action, public object names, and commit comment;
infobase/repository credentials remain on the Windows runner. Protect the
runner with `ONEC_REPOSITORY_RUNNER_TOKEN` and a host firewall rule limited to
the Docker host/container network.

The guarded workflow is:

1. `repository.status` (optionally `probe=true`);
2. `repository.lock.plan` with public 1C object names;
3. `repository.lock` with `allow_repository_lock=true`;
4. pass the returned `lock_session_id` to write preflight/apply;
5. `repository.commit.plan` and explicit `repository.commit`, or
   `repository.unlock` for only that adapter-owned session.

Apply operations are blocked for a repository-configured base unless an active
adapter-owned lock session is supplied. Structural add/delete/rename plans are
kept blocked for confirmation because parent and reference objects can also be
required.

## Configuration activation debug workflow

Activation is a separate boundary from saved-state writes and repository
coordination. The current workflow is intentionally debug-only:

1. `configuration.activation.status`;
2. `configuration.activation.plan`;
3. `configuration.activation.request`;
4. forward the returned request id to `configuration.activation.execute` with
   `mode=debug` and `confirm_activation=true`;
5. inspect or cancel the request through
   `configuration.activation.request.status`,
   `configuration.activation.request.cancel`, and
   `configuration.activation.audit`.

The request is bound to a live-SQL fingerprint and is rejected when pending
files change or the request expires. Requests and events are stored in the
adapter-local SQLite selected by `ONEC_ADAPTER_STATE_DB`; they contain no
payload bytes or credentials. `ONEC_CONFIGURATION_ACTIVATION_STATE_FILE` is a
one-time legacy JSON import source only. `configuration.activation.capabilities`
reports runner readiness without returning paths, URLs, selectors, users,
passwords, or tokens.
`configuration.activation.bridge.probe` can then check the local runner or the
authenticated HTTP runner endpoint `/configuration/activation/debug`. The
probe verifies only Designer-file availability and infobase-selector presence;
it never starts a process.
Pass `bridge_debug=true` to `configuration.activation.execute` when the runner
must also acknowledge the exact request id and live-SQL fingerprint. The runner
returns an opaque SHA-256 debug receipt; mismatched or missing receipts block
the request, while a valid receipt adds a `bridge_debug_accepted` audit event.
After a manual F7, call `configuration.activation.verify` with the same request
id. It reports `not_activated`, `changed_since_request`, or
`verified_up_to_date` from a fresh SQL comparison. The last status proves
saved/active alignment, not the historical fact that Designer performed the
activation.

Real Designer execution remains disabled. `/UpdateDBCfg` is recorded only as
the documented future base-configuration operation. Extension activation stays
manual until a separately verified platform command and post-activation check
are implemented.

Activation request mutations use SQLite `BEGIN IMMEDIATE` transactions, so
concurrent adapter processes cannot overwrite each other's request/event
updates. Saved-state backup retention is explicit:
`storage.saved_state.backups.prune` defaults to a dry run, is scoped by
`base_id`, preserves the newest requested count, and requires
`confirm_delete=true` before deleting adapter-local backup files. Backups
referenced by `metadata.write.history` are always protected; when write-history
availability cannot be verified, affected backup files are protected
fail-closed.

## Docker Run

Create a local `.env` from `.env.example`, keep real passwords outside git, and
run:

```powershell
docker compose -f plugins/1c/connector/docker-compose.yml --env-file plugins/1c/connector/.env up -d --build
```

The compose build context is `plugins/1c` because the adapter imports the
sibling `parser` package. If this service is moved to a separate repository,
copy `plugins/1c/parser` into that repository or publish it as a package.

## Standalone Extraction Checklist

When the adapter is eventually moved out of this monorepo:

1. Copy `connector/` and `parser/`.
2. Keep `contracts/openapi.yaml` versioned with releases.
3. Keep policies with the service.
4. Keep `service.yaml`, `pyproject.toml`, `Dockerfile`, `docker-compose.yml`,
   and `.env.example`.
5. Move or duplicate contract checks that assert public behavior:
   `check_1c_write_plan_contract.py`,
   `check_1c_extension_action_contract.py`,
   `check_1c_module_origin_contract.py`, and
   `check_1c_code_symbol_contract.py`.
6. Do not move RAG datasets, training configs, or agent prompts into the
   adapter service unless they become runtime dependencies.

## Live database access

### Web management

The runtime SQL connection list can be viewed and edited at
`http://<adapter-host>:8011/admin/`. The screen supports adding, editing, and
deleting entries and writes them atomically to `ONEC_SQL_BASES_JSON_FILE`
(normally `/data/onec-sql-bases.json`). Production-style deployments should set
`ONEC_ADAPTER_SERVICE_TOKEN`; the browser keeps it only in session storage. For
the isolated test profile, `ONEC_ADAPTER_ALLOW_UNAUTHENTICATED_ADMIN=true`
explicitly permits access without a token. Stored SQL passwords are never
returned by the API in either profile.

When `ONEC_SQL_BASES_JSON` is set directly, web editing is disabled because the
environment value would override the file. Move the connection map to the
configured JSON file before using the screen.

The adapter is not tied to one 1C database. Requests that read database-specific
sources must pass `base_id`; otherwise the adapter returns `base_id_required`.

### Mandatory SQL base access rule

`base_id` is the required settings key. Its entry contains the SQL server IP or
host, SQL database name, login, and password (preferably through
`password_env`). The adapter uses only that entry's existing credentials. It
must never create or change SQL logins, database users, roles, or permissions.

Application data and the metadata structure are read-only. The only SQL write
exception is a reviewed metadata saved-state change:

- base configuration metadata → `ConfigSave`;
- extension metadata → `ConfigCASSave`.

The exception does not permit writes to application-data tables, `Config`, or
`ConfigCAS`, and does not activate the saved configuration. Saved-state writes
remain gated by explicit opt-in, SHA-1 precondition, backup, transaction, and
readback verification. The binding policy is
`policies/sql-base-access-policy.yaml`.

Configure every base explicitly. Prefer `password_env` so secrets stay outside
repository files:

```json
{
  "upo_test": {
    "server": "sql-host.example.local",
    "database": "upo_test",
    "user": "configured_login",
    "password_env": "ONEC_SQL_PASSWORD_UPO_TEST"
  }
}
```

Set it as `ONEC_SQL_BASES_JSON` and pass the password separately as
`ONEC_SQL_PASSWORD_UPO_TEST`, or mount the same JSON outside the repository and
set `ONEC_SQL_BASES_JSON_FILE` to its container path. There is no implicit or
default database connection.

Current live methods:

- `query.validate`
- `query.run`
- `extensions.list`
- `schema.tables.list`
- `storage.files.list`
- `storage.file.get`
- `metadata.dbnames.summary`
- `metadata.kinds`
- `metadata.objects.list`
- `metadata.object.get`
- `metadata.object.properties`
- `metadata.object.property.write`
- `metadata.object.decode`
- `metadata.object.parts`
- `metadata.object.modules`
- `metadata.object.related`
- `metadata.object.forms`
- `metadata.object.templates`
- `metadata.object.template.details`
- `templates.read`
- `templates.analyze`
- `templates.map`
- `metadata.route.resolve`
- `metadata.form.decode`
- `metadata.object.attributes`
- `metadata.relationship.verify`
- `metadata.relationship.find`
- `metadata.object.full`
- `metadata.snapshot`
- `codec.decode`
- `codec.encode`
- `extension.objects.find`
- `modules.search`
- `modules.read`

Metadata methods require `base_id` and read live `Params`, `Config`, and
`ConfigCAS` storage through SQL. They do not use filesystem route indexes as a
source of truth. High-level metadata methods return 1C-facing data by default:
object identity, synonyms, decoded semantic sections, forms, modules, and
counts. Physical SQL table names, `_Fld...` columns, DBNames indexes, and
storage routes are internal diagnostics and are exposed only by low-level
methods (`storage.*`, `schema.*`, `query.*`, `metadata.dbnames.*`) or by
passing `include_storage=true`.

Object-scoped adapter methods accept the same public selector shapes:
`ref`, `kind` + `name`, `guid`, or MCP-friendly
`object_type`/`object_name`/`object_guid`. `ref` may use Russian or English
qualified metadata names such as `Обработка.<Name>` or `Document.<Name>`.
Client, MCP, and agent code must not add conditions for concrete object names;
the adapter owns generic selector normalization.

Saved-state client calls use the same name-first selectors together with
`layer=base_saved_state|extension_saved_state`. Public module search results
include a name-first `write_plan_target` (`ref`, form/module names, and a
1-based stream ordinal); `metadata.write.plan` resolves its physical handle
internally. SQL tables, file names, GUID owners, module handles, and payload
hashes remain diagnostic continuations exposed only with
`include_storage=true`. Every public RPC follow-up is shaped as
`{"method": "...", "params": {...}}`; `payload` is not the arguments field of
`next_call` or `next_resolution`.

Layer write policy:

- `Config` and `ConfigCAS` are **active-applied** and must be treated as read-only in adapter workflows.
- `ConfigSave` and `ConfigCASSave` are **saved, not yet applied** layers and are the only writable targets for connector staging changes.
- Base vs extension mapping:
  - base config → `ConfigSave`
  - extension config → `ConfigCASSave`
- Production apply to active layers is out of scope for this connector and requires a separate human-controlled deployment path.

Agent-facing code write rule:

- BSL edits must use `code.write`, not low-level SQL/write helpers.
- The agent passes 1C names (`object_type`/`object_name`/`routine_name`) or a
  public path such as `<extension>.<form>.<routine>` plus full code text.
- `code.write` automatically targets the saved-state layer and reports
  `write_mode.target=saved_state` with `activation_state=not_activated`.
- Write plans for embedded form-container modules return a ready
  `code.write` hint; they do not incorrectly request a nonexistent
  `#stream:<index>`.
- Use `code.read`/`code.search` with the default working state for current
  programming-time code; use `state=both` only when an explicit saved vs active
  comparison is needed.

`metadata.object.get` returns a live object card and decoded semantic sections
without physical SQL/storage traces by default. `metadata.object.decode` also
returns a 1C-facing decoded object profile by default; pass
`include_storage=true` only when adapter diagnostics need the underlying decoded
payload metadata, record containers, or DBNames/storage routes.
Exact extension objects use the same public `kind` + `name`/`ref` selectors as
base objects. `metadata.object.modules` includes owned form modules and returns
qualified names such as
`test2.Форма.t_Форма.Модуль формы`; extension GUIDs and CAS keys remain internal.

`metadata.object.properties` is the unified property endpoint for every 1C
metadata kind. It selects a kind-specific SQL decoder for `Configuration`,
`Constant`, `DocumentNumerator`, `IntegrationService`, `CommandGroup`,
`ScheduledJob`, and `DocumentJournal`, and otherwise returns the generic live
semantic profile. XML exports are analysis evidence only and are never a
runtime source for this method or any other adapter method.

`metadata.object.property.write` is the name-first saved-state writer for the
standard identity properties `synonym` and `comment`. Pass a public object
`ref` or `kind` + `name`. For an existing attribute, tabular section,
dimension, or resource, also pass `member_ref` or `member_kind` +
`member_name`; the adapter resolves the exact parent/member GUIDs and
serialized tree path internally. The method supports `plan`, `apply`,
`apply_and_verify`, and `apply_and_rollback`, requires explicit saved-state
gates, and never writes active `Config`/`ConfigCAS`. Renaming an object or
member, removing collection items, and adding a new synonym locale remain
intentionally disabled.

`metadata.object.member.add` adds one new object requisite or tabular-section
column (`Attribute`) by cloning an existing attribute in the same collection.
The caller passes only
`template_member_ref`, `new_member_name`, and optionally synonym/comment; the
adapter generates the GUID, preserves the template's type/settings, appends to
the exact declared collection, and verifies the new identity, empty/default
comment, container, and preserved non-identity settings after apply. A template
is blocked when its GUID or name is referenced outside its declared identity
fields. Arbitrary type construction and deletion are not supported by this
first structural route.

Managed form bodies in base `Config` are resolved from the public form GUID to
the sibling `<guid>.0` SQL payload. Command-bar buttons expose public command
names when their SQL binding points to a common command or a recognized
platform standard command; standard reference field `-5` is exposed as a
public `...Ref` data path. Callers never need the internal GUIDs or field codes.
Element event GUIDs are converted to platform event names (for example
`OnChange`, `ChoiceProcessing`, `AutoComplete`, `Selection`, and table row
events) and linked to their BSL handlers when the routine is present.

`metadata.object.attributes` is the preferred method for "show object
attributes/requisites" questions. It returns 1C metadata attribute names and
tabular section names from the live Config payload. For tabular sections, it
also returns decoded column names when nested column records are present.
Attributes and columns include decoded type evidence (`date`, `boolean`,
`string`, `number`, `reference`) and visible type parameters such as string
length, number precision/scale, or reference type GUID. Reference type GUIDs are
resolved back to live metadata object names and synonyms when the referenced
type exists in the base metadata. It must be preferred over SQL table/column
inspection for user-facing answers. The object can be selected by `guid`, by
`kind` + `name`, or by 1-based `ordinal` within `metadata.objects.list` for that
kind.

For a safe answer to "are these objects linked?", do not infer a link from a
similar field name, BSL mention, or a runtime value. Use
`metadata.relationship.verify` with an exact source `member` and optional
`target_ref`. It returns `confirmed` only when that member's declared 1C type
explicitly names the target object; otherwise it returns `not_confirmed` or an
explicitly ambiguous result. To discover a direct typed field without knowing
its name, call `metadata.relationship.find` with public refs only:

```json
{
  "method": "metadata.relationship.find",
  "payload": {
    "base_id": "upo_test",
    "ref": "Document.СписаниеЗапасов",
    "target_ref": "Document.РасходнаяНакладная",
    "direction": "either",
    "execution_mode": "job"
  }
}
```

`direction=either` checks both objects for explicitly declared references and
returns the direction of every confirmed edge. A `not_found` result means that
no direct declared metadata reference was found; it does not prove that an
indirect BSL, query, form, or business-process relationship is absent.

`metadata.object.full` is the preferred high-level method for agent answers like
"show everything about this document". It combines the live object card,
semantic sections, decoded forms, BSL module profiles, and counts in one
1C-facing response. Module profiles include routine lists and lightweight BSL
structural validation. Streams with BSL markers that are not complete modules
are kept, but marked as `completeness: fragment_or_invalid`. Full module text is
returned only with `include_module_text=true`. The method hides SQL/storage
traces by default; pass `include_storage=true` only for adapter diagnostics.

`metadata.object.parts` returns object part roles by evidence: metadata
payloads, form payloads, BSL stream containers, templates, and help/html
payloads. Physical Config part keys and numeric suffixes are hidden by default
and returned only with `include_storage=true`.

`metadata.object.modules` lists BSL stream modules discovered in those live
parts. Public responses use 1C-facing names such as `Модуль объекта`; physical
`module_id` values are returned only with `include_storage=true`.

`metadata.object.related` reads live related `Config` records referenced by
known object-kind sections, such as document forms and templates. Missing
references are returned explicitly with `source_missing`. Physical section paths
and Config file names are hidden unless `include_storage=true`.

`metadata.object.forms` resolves object forms through `metadata.object.related`
and then reads each form's live parts, including root `4` form payloads. Public
responses show form names and part roles; physical payload keys are hidden unless
`include_storage=true`.

`extension.objects.find` is the preferred first step for extension-specific
tasks. It searches live extension metadata by `extension`, `query`, `kind`, or
`guid`, returns object/template routes, and provides safe `read_selector`
payloads for follow-up calls. It can recover extension manifest routes even
when DBNames-Ext is incomplete; owner mismatches are returned as diagnostics
instead of silently hiding the object.

`metadata.route.resolve` resolves ConfigCAS/DBNames routes for extension
objects and child objects. Use it when a previous search returned a route
handle or when the caller has a CAS file name but needs the live object route.

`templates.read` and `templates.analyze` read MXL/MOXCEL templates by owner
selector, template selector, or direct ConfigCAS route. They return decoded
template structure: dimensions, named areas with row/column ranges, text and
parameter cells, column widths, cell text identifiers, cell parameters,
area-to-cell coverage, area intersections, shape variants, and explicit
capability flags. `merged_ranges` are reserved for authoritative merged-cell
records; until the MOXCEL merge record is decoded, possible merges are exposed
as `merged_range_candidates` with `confidence: low`.

Use `view=summary|structure|full`, `sections`, and `max_*` limits to keep
responses small for agents. `templates.map` is the compact agent-facing wrapper
over `templates.analyze`; it defaults to `view=summary` and is preferred when an
agent needs a quick layout map instead of all decoded lists.

1C templates are not only tabular MXL/MOXCEL documents. The 1C template
constructor offers these template types:

- `Табличный документ` - tabular document, MXL/MOXCEL. This is the current deep
  decoder focus.
- `Текстовый документ` - plain or structured text payload.
- `Двоичные данные` - arbitrary binary payload.
- `Active document` - Active document payload.
- `HTML документ` - HTML payload.
- `Географическая схема` - geographic schema.
- `Графическая схема` - graphical schema.
- `Схема компоновки данных` - data composition schema.
- `Макет оформления компоновки данных` - data composition appearance template.
- `Внешняя компонента` - external component payload.

Always identify the template type before applying a decoder. Current
`templates.*` decoding is evidence-first for tabular documents; non-tabular
templates should be surfaced with type, raw route, payload markers, preview, and
explicit capability gaps until dedicated decoders are implemented.

For MOXCEL reverse engineering, request `sections=moxel_records,diagnostics`
and optionally `max_moxel_records`. The response includes parser-level record
head counts, grouped head samples with `tree_position`, and coordinate-like
samples. Use `top_level_records` with a larger `max_moxel_records` to inspect
ordered MOXCEL sections around a specific tree position. These diagnostics are
not authoritative merged-cell records. For a narrow ordered window, pass
`moxel_record_start` and `moxel_record_end`, for example `431..460`.
Use `moxel_record_heads` to keep only selected top-level record head codes,
for example `1049761,1413047`. Add `moxel_record_context` to include neighbor
records around matched top-level records; context records are marked with
`match: false`. `top_level_record_summary` summarizes the returned record
window with position range, head counts, match count, and compact numeric-field
variation by head. Its `field_hints` are low-confidence labels such as
`flag_like`, `small_enum_like`, or `coordinate_or_offset_like`; use them as
navigation hints, not as authoritative MOXCEL decoding. `numeric_field_matrix`
then shows those hinted/varying field values per `tree_position` without
returning every numeric item again, and `field_runs` compresses adjacent equal
values in that matrix. `field_transitions` lists the switch points between
those runs. `cell_style_candidates` exposes inline MOXCEL text cells with
nearby scalar style evidence and following metadata nodes; treat it as a
controlled-diff aid until border/font/alignment semantics are decoded.
`top_level_shapes`
groups top-level records by structural shape
(`head`, list length, numeric/string counts) and includes sample positions.
`top_level_shape_candidates` ranks rare/long/numeric-heavy shapes as
low-confidence hints for manual layout/merge investigation. Each candidate can
include `rank` and `suggested_windows` with a ready `request_hint` for the next
focused `templates.map` call. Pass `moxel_candidate_rank` to focus
`top_level_records` on that 1-based candidate rank without copying the request
hint manually. Use `moxel_candidate_window_index` to select a later suggested
window from the same candidate when the structural shape appears more than
once. Use `moxel_candidate_reasons`, for example
`coordinate_like_prefix,long_record`, to return only candidates containing all
requested reason codes. Use `moxel_candidate_min_score` to keep only candidates
above a heuristic score threshold. `top_level_candidate_summary` reports score
and reason distributions plus the count returned after filters. Use
`moxel_candidate_heads` to filter the candidate list by head code; use
`moxel_record_heads` when filtering actual top-level records in a focused
window. Use `moxel_candidate_start`/`moxel_candidate_end` to filter candidates
by their top-level positions; use `moxel_record_start`/`moxel_record_end` when
filtering returned records.

`metadata.form.decode` decodes one form payload into an evidence-first profile:
event handlers, form items, attributes, commands, auxiliary table/command-bar
records, and the embedded form module summary. Form records include stable
paths back into the decoded tree for names, ids, localized titles, handler
names, and known platform event ids. Counts include both returned and total
record numbers so truncated responses are explicit. The profile also links form
events and form commands to module routines, links command buttons to commands
by GUID evidence, and marks handlers as `resolved` or `missing`.

`modules.search` searches live BSL text and returns snippets by default.
Physical module ids and payload coordinates are hidden unless
`include_storage=true`. Every public match includes a `read_selector` with
`method: "modules.read"` and either an object selector or an opaque
`module_ref`; agents should pass that selector to the next read call instead of
requesting storage details. When `resolve_owners=true`, results also include
`counts.owner_resolved`, `counts.owner_unresolved`, and
`diagnostics.owner_resolution` so incomplete owner recovery is explicit.
`modules.read` reads by object selector (`ref`, `guid`, `kind` + `name`,
`object_type`/`object_name`/`object_guid`, or `kind` + 1-based object
`ordinal`) and optional 1-based `module_ordinal`; it also accepts `module_ref`
from a prior search result. The response hides source and payload metadata
unless `include_storage=true`.

`code.search` is the agent-facing wrapper over module search. Its items include
`read_selector.method: "code.read"` and preserve `module_ref` when that is the
best available safe handle. `code.read` can consume that selector directly;
the selector pins the configuration view that produced the hit. A storage
stream whose Configurator-tree role is not independently decoded is returned
as `bsl_module` with `role_status=unconfirmed` and must not be treated as a
command, manager, or a tree path.

`metadata.object.commands` resolves an `extension` name to the active
extension internally before it reads the selected object. A caller provides
only the public object and extension selectors; it must not replace them with
a base-configuration route or infer a command from a BSL stream suffix. A
successful empty command list is the only evidence currently returned for “no
decoded commands”; an unresolved object route is reported separately.
For object-owned extension forms, `modules.search` and `code.search` resolve
the form module from the public owner reference. In the default
`state=working` view they inspect the saved counterpart first and fall back to
the active module only when needed; `state=active` never returns saved-only
text. Saved matches carry `activation_state=saved_state` and
`current_state.activation_state=not_activated`.
For an active extension form selector, `code.read state=both` resolves the
saved form by logical owner/form identity, even when active and saved CAS file
names differ, and reports live text SHA1 comparison evidence.

`metadata.resolve_overrides` uses the same name-first form ownership and
saved-first working-state rules. A public selector such as `Catalog.test2`
therefore resolves routines located in forms owned by that extension object;
the returned chain identifies the form and activation state without exposing
the object's physical SQL route.

`metadata.definition.find` accepts public object references such as
`Обработка.<Name>` or `Document.<Name>` in `query` and the common object
selector aliases for scoped lookup. A single metadata object match is promoted
to the top-level `object` field and the response includes `related_selectors`
for the next public calls (`metadata.object.get`,
`metadata.object.full`, `metadata.object.modules`, `metadata.object.form.details`,
`code.search`, `modules.search`, and similar selectors allowed by the object's
capabilities).

`metadata.adapter.audit` reports recognized metadata kinds, public kind counts,
missing supported kinds when `include_missing=true`, and unmapped DBNames roles
when `include_unmapped=true`. Missing entries are marked
`presence_status=supported_absent_in_selected_base`; this is a statement about
the selected infobase, not a claim that the adapter lacks that kind.

Use `python scripts/check_1c_metadata_kind_fixtures.py --live` from the
repository root to check the reproducible rare-kind fixtures. The checker does
not write SQL or create metadata. The fixture manifest pins the exact Designer
version and external reference commit. The companion
`scripts/export_1c_extension_sources.ps1` performs a read-only `test2` source
export with operating-system integrated authentication and exposes no
infobase-user or credential arguments.

`codec.decode` and `codec.encode` are low-level lossless helpers. A no-op encode
from a live source keeps the original bytes exactly; modified text/tree payloads
are encoded back using the original compression and text encoding envelope.

`changes.propose` reads one live storage payload, checks an optional
`expected_sha1`, applies `edits` to decoded brace-tree paths in memory, and
returns the re-encoded payload metadata for review. It never writes to SQL.
Each edit has `path`, `value`, optional `node_type` (`auto`, `atom`, `string`),
and optional `expected_old`. For stream payloads, an edit can use
`stream_index` with either full `text` replacement or `replace: {old, new}`,
plus optional `expected_contains`; stream headers are rebuilt with updated
byte lengths before the payload is encoded back. Diagnostic `source.module_id`
values returned by `metadata.object.modules` with `include_storage=true` or
accepted by `modules.read` can be used directly; when the module id includes
`#stream:<index>`, stream edits inherit that index unless an edit specifies its
own `stream_index`. The response
includes `validation`, produced by re-decoding the encoded proposal in memory.
For BSL stream edits, validation also runs lightweight structural checks for
routine, region, and preprocessor-block balance. Stream edits can also target
a whole BSL routine with `routine: {operation, name, text}` where operation is
`replace`, `append`, or `upsert`. Routine edits accept
`expected_old_contains` and `expected_old_sha1` as live preconditions against
the current routine text; failed preconditions reject the proposal before any
encoded review artifact is returned.

`storage.*` methods read 1C storage rows directly from SQL tables
`Params`, `Config`, `ConfigSave`, `ConfigCAS`, and `ConfigCASSave`. They are
diagnostic building blocks for the live metadata decoder; they do not create or
read filesystem indexes.

## Cache policy

The source of truth is the live database. A filesystem cache may be added only
as a derived acceleration layer for expensive decoded metadata/module payloads,
not for current table data. Cache entries must carry `base_id`, source
fingerprint, generation time, TTL, and `fresh/stale` status. If freshness cannot
be proven, the adapter must re-read live SQL or return an explicit stale-cache
error.

Operational runbook: `docs/runbooks/1c-operational-coding.md`.

## Development audit telemetry

Every REST `/rpc` call produces a privacy-safe JSONL event in
`/data/adapter-audit.jsonl`. It contains the UTC time, correlation id, public
method and selector summary, result status/error, duration, public route and
resolver timings/counts (when a write route is involved), and exception type
when the request itself fails. A `public_write_route_unresolved` event retains
the safe resolver status/error/candidate count so it can be diagnosed without
asking a caller for a module handle. It deliberately excludes BSL text, SQL
payloads, physical file names, stream indexes, credentials, and SQL connection
details. The MCP proxy forwards its generated
request id in `X-Request-ID`, so an agent response can be correlated with the
REST record. The log is shared by all configured
`base_id` values so cross-base failures and slow calls can be compared.

For development, the default retention is deliberately generous: 50 MiB per
file and ten retained files. Configure `ONEC_ADAPTER_AUDIT_MAX_BYTES` and
`ONEC_ADAPTER_AUDIT_KEEP_FILES` to change it. Rotation is best-effort and can
never fail an adapter request. A caller may supply an `X-Request-ID` header to
correlate a client event with the REST record.

The `adapter-1c-audit` Compose service writes an aggregate report every 15
minutes to `/data/adapter-audit-reports/latest.json`; set
`ONEC_ADAPTER_AUDIT_INTERVAL_SECONDS` to alter the interval. It reports base
distribution, failures, slow operations, malformed rows, and recent failures.
For an immediate manual report, run `python scripts/analyze_1c_adapter_audit.py`
against a copied log or `python /app/analyze_audit.py` inside the REST image.
The MCP proxy has its own persistent `/data/mcp-audit.jsonl` and periodic
summary: it records failures that happen before a request reaches REST.

For an extension-wide `code.search` without a concrete object selector,
`timeout_seconds` is a total search budget. If owner-route discovery consumes
that budget, the adapter returns `status=partial` with
`diagnostics.code=time_budget_exhausted`; it does not continue serial owner
probes in the background. Narrow routine work with `ref` or `kind`/`name`.

REST deployments use a five-minute Docker stop grace period. On `SIGTERM` the
adapter stops accepting new work and waits for already-running request threads,
including verified saved-state writes, to complete. Do not deploy the REST
service while an operator is intentionally running a production-base write;
the deployment prevents a half-response, but the client should still retry only
after it receives a structured result.
The deployment script also waits for `health.runtime.active_rpc_count=0` before
recreating REST. `-SkipDrainCheck` is an emergency-only override and must not
be used while a write is in progress.
