Complete name-first 1C adapter saved-state support

This commit is contained in:
2026-07-26 16:39:53 +03:00
parent b8c62fa8fa
commit aed134d817
44 changed files with 15436 additions and 697 deletions
+301 -23
View File
@@ -603,6 +603,22 @@ Important methods:
resolved through the platform `_BPr<N>` route internally.
- `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
@@ -722,8 +738,12 @@ Important methods:
- `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. Common-module writes, when
explicitly enabled, remain limited to the saved-state layer.
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`,
@@ -752,6 +772,95 @@ Important methods:
`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.
- 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
@@ -800,7 +909,10 @@ Important methods:
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.
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
@@ -816,6 +928,22 @@ python scripts/audit_1c_adapter_coverage.py --base-id upo_test --output reports/
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:
@@ -1967,28 +2095,63 @@ Selector rules:
`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`.
- 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 payload and its active SQL source. It accepts `table` +
`file_name` or `module_ref`, 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`.
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 overview for a whole save
layer. It accepts `table=ConfigSave|ConfigCASSave`, compares saved rows with
`Config`/`ConfigCAS` by `FileName`, `PartNo`, size, and SHA1, and reports
counts plus per-file statuses: `changed`, `unchanged`, or `saved_only`.
- `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.
- `metadata.saved_state.changes.list` is the read-only pending-change overview
across `ConfigSave` and `ConfigCASSave` by default. It returns changed and
saved-only files with `diff_selector` links, or includes unchanged files when
`include_unchanged=true`. Pass `include_context=true` for best-effort
form/module context by file; the freshness of the pending-change list remains
live SQL verified, while context is explanatory metadata. Pass
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.
Each group includes compact `selectors`: per-file `diff` selectors, plus
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
@@ -2209,9 +2372,12 @@ Purpose:
- 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 `module_ref`. The saved-state module search accepts
`object_type`/`object_name`/`object_guid` as public selectors and resolves
names to `owner_guid` when possible. Its matching streams return
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.
@@ -2253,6 +2419,111 @@ Output schema:
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;
@@ -2285,6 +2556,13 @@ Purpose:
`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
@@ -2297,8 +2575,8 @@ Purpose:
- 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 future non-form writers behind explicit target routing instead of
ad-hoc direct SQL methods.
- keep remaining writers behind explicit target routing instead of ad-hoc
direct SQL methods.
## Metadata Write Preflight