Initial project import
This commit is contained in:
+340
-32
@@ -2,22 +2,18 @@
|
||||
|
||||
## Configuration repository control
|
||||
|
||||
The current adapter release is SQL-only. It does not start Designer, call a
|
||||
Windows runner, or inspect repository internals. External 1C execution is a
|
||||
future-version capability and is disabled by default with
|
||||
`ONEC_ADAPTER_ENABLE_EXTERNAL_1C=false`.
|
||||
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`: the base runtime profile
|
||||
declares `repository.backend=direct|karman_bridge`, the Designer executable,
|
||||
infobase selector, endpoint, optional extension, users, and environment-variable
|
||||
names containing transient passwords. No repository, bridge, or endpoint name is
|
||||
hard-coded or inferred from naming conventions.
|
||||
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=automatic|manual` is also selected per base. Automatic
|
||||
mode uses the configured runner. Manual mode requires no Designer or runner:
|
||||
`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
|
||||
@@ -43,13 +39,11 @@ 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.
|
||||
|
||||
Both backends invoke standard Designer repository commands. A Karman/Filebox
|
||||
backend is an opaque native TCP transport and does not own credentials, object
|
||||
locks, or repository transactions. For configured bases, saved-state apply is
|
||||
blocked until the caller supplies an active adapter-owned `lock_session_id`.
|
||||
Commit additionally requires `allow_repository_commit=true` and a non-empty
|
||||
version comment. Unlock and commit operate only on the object set recorded for
|
||||
that adapter session.
|
||||
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.
|
||||
|
||||
@@ -64,6 +58,30 @@ Related work plan: `docs/1c-extension-layer-plan.md`.
|
||||
- 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.
|
||||
|
||||
## User Identity And Access Terminology
|
||||
|
||||
@@ -123,8 +141,8 @@ Password mutation is available only for the platform `infobase_user` layer:
|
||||
`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`; this also permits an
|
||||
unprotected runtime bridge endpoint and must never be enabled in production.
|
||||
`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
|
||||
@@ -411,6 +429,64 @@ Purpose:
|
||||
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
|
||||
@@ -428,11 +504,21 @@ storage rows, payload paths, or `metadata.module.write_apply`.
|
||||
|
||||
Contract:
|
||||
|
||||
- default `mode` is `apply`, and apply means save to the working
|
||||
`ConfigSave`/`ConfigCASSave` layer, not production apply;
|
||||
- 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`;
|
||||
@@ -455,7 +541,9 @@ Contract:
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -601,6 +689,22 @@ Important methods:
|
||||
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
|
||||
@@ -728,6 +832,10 @@ Important methods:
|
||||
`Модуль сервиса интеграции`. 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
|
||||
@@ -859,6 +967,15 @@ Important methods:
|
||||
`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
|
||||
@@ -905,7 +1022,11 @@ Important methods:
|
||||
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
|
||||
may set `source.kind=code_read`, but it must preserve the module `origin`
|
||||
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
|
||||
@@ -1000,6 +1121,9 @@ Working source state:
|
||||
`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` /
|
||||
@@ -2103,6 +2227,15 @@ Selector rules:
|
||||
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
|
||||
@@ -2134,6 +2267,74 @@ Selector rules:
|
||||
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
|
||||
@@ -2178,7 +2379,11 @@ Freshness statuses:
|
||||
- `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:
|
||||
|
||||
@@ -2195,24 +2400,91 @@ RPC methods:
|
||||
}
|
||||
```
|
||||
|
||||
- `metadata.code_index.status`: reports cache/module/vector chunk counts;
|
||||
- `metadata.code_index.search`: fast lexical search over cached BSL, verifying
|
||||
candidates by default;
|
||||
- `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_vector.search`: searches cached module/routine chunks with
|
||||
local hashing embeddings or supplied `query_embedding`, then revalidates by
|
||||
default.
|
||||
- `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`: cache plus SQL hash verification;
|
||||
- `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` or read live SQL again.
|
||||
`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
|
||||
|
||||
@@ -2353,12 +2625,23 @@ 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
|
||||
@@ -2967,6 +3250,31 @@ Purpose:
|
||||
- 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:
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
Date: 2026-06-20
|
||||
|
||||
The maintained evidence log for saved-state encoding, extension overlays,
|
||||
Configurator reload behaviour, and failed protocol paths is split by topic in
|
||||
[`docs/1c-sql-protocol/`](1c-sql-protocol/README.md). This specification keeps
|
||||
only universal format rules.
|
||||
|
||||
This document describes universal 1C SQL storage rules observed and verified so
|
||||
far. It must not contain knowledge about a particular infobase object such as
|
||||
`АвансовыйОтчет`, except as test evidence in a separate report.
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# SQL protocol knowledge base for 1C configuration storage
|
||||
|
||||
This directory is the durable, evidence-first record for the 1C SQL adapter.
|
||||
It complements [the general format specification](../1c-sql-format-spec.md).
|
||||
Do not put all observations into one chronological note: add a fact to the
|
||||
document for its layer, object type, codec, operation, or experiment.
|
||||
|
||||
## Non-negotiable runtime rule
|
||||
|
||||
The adapter works **only through SQL**. It may decode and encode only what was
|
||||
observed in the target database and proven by round-trip checks. It must never
|
||||
invent a metadata object, physical path, BSL fragment, form element, join,
|
||||
codec, or cryptographic value. Unknown or ambiguous work returns an explicit
|
||||
unsupported/protocol-incomplete result.
|
||||
|
||||
The Configurator remains a human-operated consumer of the saved-state overlay.
|
||||
The adapter does not start it, automate it, or claim to change its in-memory
|
||||
state.
|
||||
|
||||
## Map
|
||||
|
||||
- [Rules and evidence discipline](rules/evidence-and-safety.md)
|
||||
- [Agent contract: do not invent](rules/agent-contract.md)
|
||||
- [Configuration SQL layers](layers/configuration-storage.md)
|
||||
- [Extension saved-state overlay](extensions/saved-state-overlay.md)
|
||||
- [Common form and BSL module handling](objects/common-form-module.md)
|
||||
- [Object component graph and child selectors](objects/component-graph.md)
|
||||
- [Report object-module carrier](objects/report-object-module.md)
|
||||
- [Payload envelope codec](codecs/payload-envelope.md)
|
||||
- [`__configinfo` map and service atom](codecs/configinfo.md)
|
||||
- [Configurator refresh behaviour](operations/configurator-cache.md)
|
||||
- [Reproducible `upo_test/test2` experiment](experiments/upo-test-test2.md)
|
||||
- [Known dead ends and prohibited shortcuts](research/known-dead-ends.md)
|
||||
- [Template for the next experiment](templates/experiment-record.md)
|
||||
- [Current adapter component map](implementation/adapter-components.md)
|
||||
|
||||
## Maintenance rule
|
||||
|
||||
After every material protocol investigation, record all three outcomes:
|
||||
|
||||
1. proven direction and the exact evidence;
|
||||
2. failed direction and why it failed;
|
||||
3. remaining unknowns and the next safe experiment.
|
||||
|
||||
Keep raw payloads and credentials out of git. Reference private learning
|
||||
artifacts by opaque ID or SHA-1 only.
|
||||
@@ -0,0 +1,35 @@
|
||||
# `__configinfo`: file map and service atom
|
||||
|
||||
## Proven map
|
||||
|
||||
The decoded `E__configinfo` text contains logical file-name pairs:
|
||||
|
||||
```text
|
||||
"<object-guid>.0",<base64 of 20-byte SHA-1>
|
||||
```
|
||||
|
||||
The Base64 value is the SHA-1 of the complete raw `BinaryData` stream for that
|
||||
logical file. Updating a module without replacing this map value produces the
|
||||
Configurator error “Ошибка хеш-версии файла конфигурации”.
|
||||
|
||||
The map rewrite is deterministic and losslessly round-trip proven for the
|
||||
studied extension form module.
|
||||
|
||||
## Service atom: current status
|
||||
|
||||
The root block also contains three observed binary atoms: two 48-byte values
|
||||
and a 32-byte value. The 32-byte value changed across manual saves, including
|
||||
when the module bytes returned exactly to a previous SHA-1. A random mutation
|
||||
of its variable part triggered a platform licensing/crypto error.
|
||||
|
||||
Therefore:
|
||||
|
||||
- preserve all service atoms byte-for-byte during the proven incremental
|
||||
protocol;
|
||||
- do not synthesise, randomise, zero, or “recalculate” them;
|
||||
- do not claim their reverse codec is known;
|
||||
- record new observations in an experiment file before changing this rule.
|
||||
|
||||
The `test2` map-update experiment activated successfully while preserving the
|
||||
existing service atom. This is activation evidence for preservation, not for
|
||||
generation.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Payload envelope codec
|
||||
|
||||
## Observed codec pipeline
|
||||
|
||||
For the studied configuration payloads:
|
||||
|
||||
```text
|
||||
SQL BinaryData
|
||||
→ raw-deflate (or detected alternative envelope)
|
||||
→ UTF-8 text, sometimes BOM
|
||||
→ brace-based 1C serialized value tree / container text
|
||||
```
|
||||
|
||||
The decoder must retain envelope type, original encoding, BOM, line endings,
|
||||
and all unmodified text. An encoder is valid only if an unchanged
|
||||
decode→encode cycle returns byte-identical payload bytes.
|
||||
|
||||
## Lossless transform discipline
|
||||
|
||||
1. Decode source bytes.
|
||||
2. Identify the exact BSL or value-tree slice with a proven extractor.
|
||||
3. Replace only that slice.
|
||||
4. Encode with the original codec metadata.
|
||||
5. Calculate SHA-1 from the final raw stored bytes, never from decoded text.
|
||||
|
||||
Do not canonicalize braces, whitespace, strings, BOM, compression level, or
|
||||
base64 blocks without an independently proven canonical writer.
|
||||
@@ -0,0 +1,72 @@
|
||||
# `upo_test` / `test2` saved-state experiment
|
||||
|
||||
## Scope
|
||||
|
||||
- Test base: `upo_test`
|
||||
- Extension: `test2`
|
||||
- Extension GUID: `fb26cf42-7609-11f1-828f-005056b0d483`
|
||||
- Common form: `t_Форма`
|
||||
- Form GUID: `77494708-43ea-4956-ac3c-199cfb035ad2`
|
||||
- Independently manually edited form: `tt_Форма3`
|
||||
- Form GUID: `99590008-addf-49fa-9ada-24962756d0cf`
|
||||
|
||||
These are experiment identifiers, not a universal hardcoded route.
|
||||
|
||||
## Follow-up cross-extension confirmation (2026-08-13)
|
||||
|
||||
The same logical-file protocol was exercised on a different extension and
|
||||
object class:
|
||||
|
||||
- Extension: `фс_Отчеты`
|
||||
- Extension GUID: `9b11f844-3d08-11f1-8287-005056b0d483`
|
||||
- Object: `Report._ПоступлениеТовара`
|
||||
- Object GUID: `c428f629-785a-4141-b038-f2192bb4580d`
|
||||
- Guarded change: `//Пример - 3` → `//Тест - 3`
|
||||
|
||||
The active root was a hash-keyed `ConfigCAS` record, but decoding it exposed
|
||||
the logical `O` / `O.0` map and the SHA-1 of each active payload. The adapter
|
||||
created exactly `E__configinfo`, `E__O`, `E__O.0`, executed a public
|
||||
`code.write` replacement and reread it. A controlled `apply_and_rollback`
|
||||
passed before `apply_and_verify`. A human then confirmed `//Тест - 3` in the
|
||||
Configurator extension editor.
|
||||
|
||||
This proves the canonical object-module overlay route for this observed
|
||||
extension-root family. The implementation must retain the suffix of the exact
|
||||
resolved BSL stream: a separately diagnosed production route for
|
||||
`Report.УОП_ПечатьЦенниковАссортимента` resolves its manager module as `.2`,
|
||||
not `.0`. This does not authorize a guessed suffix: the selected `O.S` entry
|
||||
and its SHA-1 must be present in the decoded root map. Forms or an unknown
|
||||
root that cannot be decoded into that exact logical map remain unproven.
|
||||
|
||||
## Outcomes
|
||||
|
||||
1. Writing a `.0` module alone made a comment visible after a reload but
|
||||
failed activation with a hash-version error.
|
||||
2. Pairing the module write with the matching `__configinfo` map update made
|
||||
the comment activate into `ConfigCAS`; Configurator cleared
|
||||
`ConfigCASSave` after successful application.
|
||||
3. Building a complete three-file initial overlay while Configurator was
|
||||
closed, then opening and applying it, succeeded for `change-10`.
|
||||
4. A manual save in `tt_Форма3` produced a three-row pending overlay for that
|
||||
form plus `__configinfo`. Incrementally adding `t_Форма` descriptor/module
|
||||
while preserving the existing rows and map entries kept both edits.
|
||||
5. A pending `__configinfo` map can point at a newer active module than an
|
||||
earlier inspection of `ConfigCAS`; selecting the source through the pending
|
||||
map avoids duplicate BSL comments.
|
||||
|
||||
## Failed paths retained as evidence
|
||||
|
||||
- Raw module-only write: invalid collection hash.
|
||||
- Random service-atom bytes: licensing/crypto error.
|
||||
- Treating the active module as authoritative while a pending map exists:
|
||||
duplicate comment insertion.
|
||||
- Assuming UI text means SQL save: the editor buffer can differ from
|
||||
`ConfigCASSave`; verify SQL immediately after saving.
|
||||
|
||||
## Verification after every apply
|
||||
|
||||
1. `ConfigCASSave` for the extension becomes empty.
|
||||
2. Extension root key in `_ExtensionsInfo`/`ConfigCAS` changes to the new
|
||||
`__configinfo` SHA-1.
|
||||
3. Active `ConfigCAS` contains the requested BSL text.
|
||||
4. The active map points to the SHA-1 of the active module bytes.
|
||||
@@ -0,0 +1,128 @@
|
||||
# Extension saved-state overlay (`ConfigCASSave`)
|
||||
|
||||
> **Deployment and integration-test target:** `adapter-1c-mcp` runs on
|
||||
> `docker.cin.su`. Use that host for deployment and live adapter checks;
|
||||
> `test-docker` is staging only and requires an explicit request.
|
||||
|
||||
## Canonical object-module overlay: activation evidence
|
||||
|
||||
For extension GUID `E` and object GUID `O`, the overlay uses:
|
||||
|
||||
```text
|
||||
E__configinfo
|
||||
E__O
|
||||
E__O.S
|
||||
```
|
||||
|
||||
This route is now human-confirmed in two independently selected extension
|
||||
object-module cases on `upo_test`:
|
||||
|
||||
| Extension | Object | Public test | Human confirmation |
|
||||
|---|---|---|---|
|
||||
| `test2` | Common form/module fixture | prior `change-10` experiment | Configurator applied overlay into `ConfigCAS` |
|
||||
| `фс_Отчеты` | `Report._ПоступлениеТовара` object module | `//Пример - 3` → `//Тест - 3` | Configurator displayed `//Тест - 3` in the extension editor |
|
||||
|
||||
The descriptor and module are separate objects. A pending extension may contain
|
||||
only rows changed manually by the user; the root `__configinfo` map can still
|
||||
refer to unchanged active parts. An adapter must preserve those rows and all
|
||||
their map entries.
|
||||
|
||||
For the separately observed hash-keyed overlay, the equivalent boundary is the
|
||||
live extension `root_cas_key`: an automatic first-write copy must include that
|
||||
root manifest together with the selected object parts. A leaf module key alone
|
||||
is not a complete working-copy selection boundary.
|
||||
|
||||
## Hash-keyed first-write status
|
||||
|
||||
In `upo_test` / `фс_Отчеты`, copying the complete evidenced hash-key group
|
||||
from `ConfigCAS` to `ConfigCASSave` under unchanged hash names produced a
|
||||
byte-for-byte SQL readback, but Configurator continued to read the active
|
||||
`ConfigCAS` module. Therefore a same-name hash copy is **not** a working-copy
|
||||
protocol.
|
||||
|
||||
The active extension root has since been decoded as the equivalent logical
|
||||
file map: it contains `"<object-guid>[.suffix]",Base64(SHA-1(payload))`
|
||||
pairs, including the target descriptor and the selected BSL module. For the evidenced
|
||||
object-module family the initial overlay maps exactly three active files to:
|
||||
|
||||
```text
|
||||
root manifest -> E__configinfo
|
||||
object descriptor -> E__O
|
||||
object `.S` module -> E__O.S, where `.S` is the suffix of the exact resolved
|
||||
BSL stream (for example `.0` or `.2`)
|
||||
```
|
||||
|
||||
The adapter must reject any incomplete route and must never fall back to
|
||||
unchanged hash names. The map and all service atoms are copied verbatim on the
|
||||
first overlay; subsequent writes update only the proven file-SHA reference.
|
||||
|
||||
### Exact prepare/write algorithm
|
||||
|
||||
1. Resolve the extension, public object `O`, active descriptor and active `.S`
|
||||
BSL stream from the extension manifest.
|
||||
2. Decode the selected active root. Require exactly one map entry for `O` and
|
||||
exactly one for the selected `O.S`; require their SHA-1 values to equal the selected
|
||||
active descriptor/module payloads.
|
||||
3. If the overlay is absent, atomically copy only those three sources as
|
||||
`E__configinfo`, `E__O`, and `E__O.S`. Do not copy the whole extension.
|
||||
4. If it exists, preserve pending rows and maps; never overwrite another
|
||||
change. Prepare may add only missing object parts.
|
||||
5. Replace BSL only after the exact old fragment, source SHA-1, extension,
|
||||
public object and module stream agree. For an object module, update the one
|
||||
`O.S` SHA-1 value in `E__configinfo` in the same transaction and preserve
|
||||
service atoms byte-for-byte.
|
||||
6. Re-read the BSL and both changed rows. `apply_and_rollback` must restore
|
||||
the module and remove adapter-created first-overlay rows.
|
||||
|
||||
`code.write` callers provide only public selectors and the replacement. The
|
||||
adapter owns storage mapping, preparation, paired update and rollback.
|
||||
|
||||
## Rejected approaches (retain as regression hazards)
|
||||
|
||||
- Copying `ConfigCAS` hash rows to `ConfigCASSave` under the same names. It
|
||||
produced correct SQL readback but Configurator ignored it for `фс_Отчеты`.
|
||||
- Writing only the BSL module payload. The map continues to point to the old
|
||||
SHA-1 and Configurator reports a collection/hash-version error.
|
||||
- Generating `__configinfo`, its service atoms, or logical file mappings from
|
||||
names alone. The adapter must first decode the actual selected root map.
|
||||
- Replacing the entire compressed module container. Only the declared BSL
|
||||
prefix codec is allowed; opaque stream tail bytes must remain unchanged.
|
||||
- Resolving an already selected object-module path through generic metadata
|
||||
path traversal. Its module suffix is a BSL container, so this can fail before the
|
||||
saved-state writer sees the exact module route.
|
||||
|
||||
## Proven incremental write protocol
|
||||
|
||||
For a form module change:
|
||||
|
||||
1. Read the current `E__configinfo` from `ConfigCASSave` if it exists;
|
||||
otherwise derive a complete initial overlay from active `ConfigCAS`.
|
||||
2. Resolve the form descriptor and module hashes from that map.
|
||||
3. Patch the module with a unique BSL anchor and lossless payload codec.
|
||||
4. Replace exactly the Base64(SHA-1(raw module bytes)) value paired with
|
||||
logical name `O.0` in `E__configinfo`.
|
||||
5. Preserve every unrelated map entry and service atom byte-for-byte.
|
||||
6. In one transaction insert missing `E__O`/`E__O.0` rows and update
|
||||
`E__configinfo`, with compare-and-set SHA-1 preconditions.
|
||||
|
||||
The adapter core exposes a pure `build_extension_saved_state_pair_plan` helper
|
||||
for steps 3–5. It refuses a plan when the ConfigInfo map does not reference the
|
||||
current saved stream, so a later transaction cannot silently overwrite a
|
||||
divergent human overlay.
|
||||
|
||||
For an already existing single-part pending stream, the paired writer locks the
|
||||
module and `__configinfo`, verifies both preconditions, writes both payloads
|
||||
and their `DataSize` values in one transaction, keeps independent rollback
|
||||
evidence, and verifies both rows afterwards. `__configinfo` itself is never a
|
||||
primary editable stream. Missing overlay rows still use the separate prepare
|
||||
route before this writer may update them.
|
||||
|
||||
This protocol was activation-proven in the `test2` experiment. It is not yet
|
||||
a universal proof for every 1C platform version or every extension object
|
||||
class; new classes require their own evidence record.
|
||||
|
||||
## Important overlay behaviour
|
||||
|
||||
An external SQL overlay write does not set the Configurator’s in-memory dirty
|
||||
flag. Its visibility therefore depends on the current session. See
|
||||
[Configurator refresh behaviour](../operations/configurator-cache.md).
|
||||
@@ -0,0 +1,24 @@
|
||||
# Current adapter component map
|
||||
|
||||
This map says where to extend the project without losing protocol boundaries.
|
||||
It is not a substitute for reading the linked source before editing it.
|
||||
|
||||
| Component | Location | Responsibility | Evidence boundary |
|
||||
|---|---|---|---|
|
||||
| RPC/service orchestration | `plugins/1c/connector/adapter_1c_server.py` | Base resolution, live SQL calls, saved-state plans/apply, verification, backups | Must keep active layers read-only |
|
||||
| Payload codec | `plugins/1c/parser/payload.py` | Envelope detection, decompression, lossless encode metadata | Require byte-identical unchanged round trip |
|
||||
| Brace parser | `plugins/1c/parser/*` | Decode 1C serialized brace/value trees | A parse tree is not semantic proof by itself |
|
||||
| Extension routes | `plugins/1c/parser/extensions.py` and adapter extension resolvers | `_ExtensionsInfo` and `ConfigCAS` manifest routes | Never infer part role from suffix alone |
|
||||
| DBNames mapping | `plugins/1c/parser/dbnames.py` | Metadata-to-physical SQL role evidence | Names/roles must come from live DBNames evidence |
|
||||
| Storage access | adapter storage helpers | Single/multi-part row reads, hashes, paired extension map updates, compare-and-set and backups | Writes only to allowed saved-state tables |
|
||||
| Policy | `plugins/1c/connector/policies/` | Runtime boundary and base-access rules | Policy must match tested capability, not aspiration |
|
||||
| Regression tests | `tests/1c/` and `scripts/smoke_1c_*` | Preserve routes and observed protocol rules | Add fixture/test before promoting a decoder rule |
|
||||
|
||||
## Required update order for a new object type
|
||||
|
||||
1. Add an experiment record with before/after SQL evidence.
|
||||
2. Add/extend a decoder fixture and test.
|
||||
3. Add the narrow codec or route resolver.
|
||||
4. Add a writer only after lossless round-trip, paired-index handling, rollback,
|
||||
and human activation verification.
|
||||
5. Update the relevant topic document and this map if ownership changed.
|
||||
@@ -0,0 +1,39 @@
|
||||
# Configuration storage layers
|
||||
|
||||
The following SQL tables have the same observed storage-row shape:
|
||||
|
||||
`FileName`, `Creation`, `Modified`, `Attributes`, `DataSize`, `BinaryData`,
|
||||
`PartNo`.
|
||||
|
||||
| Layer | Role | Direct adapter writes |
|
||||
|---|---|---|
|
||||
| `Config` | Active base configuration | Forbidden |
|
||||
| `ConfigSave` | Pending base configuration changes | Controlled `upo_test` only |
|
||||
| `ConfigCAS` | Active extension content-addressed store | Forbidden |
|
||||
| `ConfigCASSave` | Pending extension configuration overlay | Controlled `upo_test` only |
|
||||
|
||||
`PartNo` must be read and preserved. The currently proven writer handles a
|
||||
single-part stream only; a multi-part stream is unsupported until a
|
||||
table-aware round-trip codec exists.
|
||||
|
||||
## Addressing rules
|
||||
|
||||
Base `Config` objects are commonly addressed by metadata GUID. Extension active
|
||||
objects are reached through `_ExtensionsInfo` → extension root in `ConfigCAS`
|
||||
→ root manifest → object part SHA-1 key. Do not infer a semantic role from a
|
||||
suffix such as `.0`; inspect the payload and route evidence.
|
||||
|
||||
For normal content-addressed rows, `ConfigCAS.FileName` was observed to equal
|
||||
SHA-1 of the stored bytes. Always compute and compare the hash rather than
|
||||
trusting the name: an activation experiment left a legacy alias whose name was
|
||||
an old key while its bytes had the new SHA-1.
|
||||
|
||||
## Write sequence
|
||||
|
||||
1. Resolve public object/form/module against live SQL.
|
||||
2. Read active bytes and any existing saved-state rows.
|
||||
3. Prepare only missing saved rows; never overwrite a user’s existing rows.
|
||||
4. Encode the changed stream losslessly.
|
||||
5. Update every proved companion index in the same SQL transaction.
|
||||
6. Read back and validate hashes and semantic exact-match count.
|
||||
7. Tell the human which Configurator scope to reload.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Common form modules
|
||||
|
||||
## Proven form facts
|
||||
|
||||
The `test2` common forms `t_Форма` and `tt_Форма3` are distinct objects with
|
||||
distinct GUIDs and descriptors. A label must be decoded from the descriptor;
|
||||
do not shorten or normalize it by guesswork (`tt_Форма3` is not `t_Форма3`).
|
||||
|
||||
A form `.0` payload can contain more than a simple standalone BSL string. The
|
||||
adapter must use the proven container extractor and preserve all non-BSL
|
||||
segments, including form settings and command metadata.
|
||||
|
||||
## Module patch rules
|
||||
|
||||
- Obtain the BSL region from the decoded container, not from a global text
|
||||
search over compressed bytes.
|
||||
- Count the requested anchor in the relevant BSL region.
|
||||
- Replace a unique exact fragment once; report ambiguity otherwise.
|
||||
- Re-encode using the source payload’s detected codec and line-ending style.
|
||||
- Read back, re-extract BSL, and verify the target occurrence count.
|
||||
|
||||
## Pending-state resolution
|
||||
|
||||
When `ConfigCASSave` contains a `__configinfo` map, that map is authoritative
|
||||
for the pending overlay even if the corresponding module row is absent. The
|
||||
active `ConfigCAS` source must be selected through the saved map before a new
|
||||
delta row is created. Reading only active content can incorrectly conclude
|
||||
that a pending comment is absent and cause a duplicate insertion.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Граф составляющих объекта конфигурации
|
||||
|
||||
Объект конфигурации — это корень, а не минимальная единица работы. Его
|
||||
составляющие: модуль объекта, формы и их модули, макеты, схема компоновки
|
||||
данных (СКД), а после декодирования СКД — наборы данных, запросы, поля,
|
||||
ресурсы и варианты. Для записи нужен адрес именно составляющей и доказанный
|
||||
физический носитель этой составляющей.
|
||||
|
||||
## Публичная инвентаризация
|
||||
|
||||
`metadata.object.components` — read-only фасад над уже доказанными SQL
|
||||
декодерами. Он принимает обычный селектор владельца (`ref` либо `kind` и
|
||||
`name`, при необходимости `extension`) и возвращает граф:
|
||||
|
||||
- корень `metadata_object`;
|
||||
- только реально найденные `module`, `form`, `template` и `scd`;
|
||||
- публичные `path` и `read_selector` каждого потомка;
|
||||
- `unresolved` для областей, которые не были подтверждены.
|
||||
|
||||
Метод не создаёт потомок потому, что он обычно есть у такого типа объекта.
|
||||
Например, пустой ответ `metadata.object.modules` у общей формы означает
|
||||
«модуль этим маршрутом не найден», а не повод назвать форму модулем.
|
||||
|
||||
Пример запроса:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "metadata.object.components",
|
||||
"payload": {
|
||||
"base_id": "upo_test",
|
||||
"extension": "test2",
|
||||
"ref": "Report.tt_Отчет",
|
||||
"include_storage": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`read_selector` можно передать следующему read-only методу. Это не является
|
||||
разрешением на запись: запись выбирает компонент по его `path`, повторно
|
||||
подтверждает конкретный carrier/stream и разрешается только для семейства
|
||||
контейнеров с доказанным byte-preserving кодеком.
|
||||
|
||||
## Иерархия и кодеки
|
||||
|
||||
Логический BSL один и тот же независимо от владельца, но его физический
|
||||
носитель может различаться. Поэтому не нужны отдельные эвристики «для формы»
|
||||
и «для отчёта»: нужен реестр семейств carrier-кодеков. Один кодек применяется
|
||||
к нескольким типам объектов лишь после доказательства одинаковых границ,
|
||||
непрозрачных атомов и round-trip проверки.
|
||||
|
||||
Текущий пример: модуль общего формы в `upo_test/test2` имеет доказанный
|
||||
write-кодек saved-state; обнаруженный поток модуля отчёта — только
|
||||
read-only structural codec. Второй компонент может быть найден и прочитан,
|
||||
но не получит разрешение на SQL-запись, пока его служебный хвост не будет
|
||||
декодирован.
|
||||
@@ -0,0 +1,115 @@
|
||||
# Модуль объекта отчёта в расширении
|
||||
|
||||
Статус: чтение, точное разрешение владельца и контролируемая запись короткого
|
||||
фрагмента поддержаны для доказанного hash-keyed saved-state маршрута.
|
||||
|
||||
Наблюдение в `upo_test`, расширение `test2`, отчёт `tt_Отчет`: сохранённый файл
|
||||
`<extension-guid>__<report-guid>.0` является `raw_deflate` контейнером из пяти
|
||||
потоков. BSL-модуль расположен в потоке `4`.
|
||||
|
||||
Поток начинается читаемым UTF-8-комментарием, но последующий текст содержит
|
||||
нулевые байты и смешанное представление символов. Общий потоковый декодер
|
||||
позволяет найти комментарий, однако его обратное кодирование меняет байты
|
||||
неизменённого хвоста BSL. Экспериментальная запись показала это в Конфигураторе
|
||||
и была немедленно восстановлена из парной резервной копии.
|
||||
|
||||
Правило: наличие читаемого BSL-фрагмента не доказывает возможность записи.
|
||||
Для потока с `NUL` адаптер возвращает
|
||||
`mixed_encoding_module_stream_unsupported` и не создаёт SQL-изменений.
|
||||
Это не означает, что для каждого отчёта нужен свой кодер: один доказанный
|
||||
кодек может обслуживать все модули с одинаковым физическим носителем.
|
||||
|
||||
Два ручных образца определили безопасную границу записи: редактируется только
|
||||
объявленный UTF-8-префикс, а непрозрачный хвост и остальные потоки сохраняются
|
||||
побайтно. Для hash-keyed overlay рабочий слой создаётся доказанным копированием
|
||||
подтверждённых ключей `ConfigCAS → ConfigCASSave`; `__configinfo` для него не
|
||||
создаётся и не предполагается.
|
||||
|
||||
Текущая реализация `parser.cas_payload.stream_blocks_with_data` ищет похожие
|
||||
заголовки регулярным выражением по всему распакованному буферу. В потоке
|
||||
отчёта такие последовательности встречаются и внутри данных, поэтому это
|
||||
эвристика для чтения, а не структурный декодер. Нельзя использовать её индекс
|
||||
потока как основание для обратной записи.
|
||||
|
||||
Структурный read-only декодер `decode_declared_utf8_bsl_prefix` подтверждён на
|
||||
этом образце: пять последовательных блоков; пятый имеет `declared_1 = 68` и
|
||||
`declared_2 = 512`. Первые 68 байт — UTF-8 BOM и точный BSL-текст, оставшиеся
|
||||
444 байта — непрозрачный служебный хвост. Декодер вернул только:
|
||||
`// protocol-report-baseline-1` и `// protocol-report-manual-change-4`.
|
||||
|
||||
## Пара ручных образцов `2 → 3`
|
||||
|
||||
Образцы `samples/manual-change-2.json` и `samples/manual-change-3.json`
|
||||
содержат raw-deflate байты, сохранённые человеком в Конфигураторе. В
|
||||
распакованном контейнере длиной 1283 байта замена цифры `2` на `3` изменила
|
||||
BSL ровно в смещении `838` (`0x32 → 0x33`). Одновременно платформа изменила
|
||||
шесть служебных диапазонов: `110..113`, `230..252`, `437..464`, `590..593`,
|
||||
`598..601`, `716..719`. Трёхбайтовое значение повторяется в нескольких
|
||||
местах, а два диапазона содержат связанные Base64-представления.
|
||||
|
||||
Это доказывает, что нельзя перепаковывать поток общим writer'ом. Отдельный
|
||||
fixed-width кодек меняет только первые `declared_1` байт: короткий текст
|
||||
дополняется пробелами внутри этого поля, хвост и размер члена не меняются.
|
||||
Рост префикса или структурная правка процедуры явно отклоняются.
|
||||
|
||||
## Полный объявленный поток: переменная длина
|
||||
|
||||
Нельзя переносить ограничение fixed-width с описанного выше носителя на все
|
||||
объектные BSL-модули. На рабочем маршруте `upo / фс_Отчеты /
|
||||
Report.УОП_ПечатьЦенниковАссортимента / .2 / stream:4` подтверждён другой
|
||||
контейнер: у выбранного BSL-потока `declared_1 == declared_2 == 36101` и
|
||||
`opaque_tail_bytes == 0`. Это полный UTF-8 поток, а не префикс перед
|
||||
непрозрачными данными.
|
||||
|
||||
Для такого носителя адаптер использует обычный структурный stream writer:
|
||||
он меняет текст, пересобирает оба объявленных размера в заголовке и сдвигает
|
||||
только последующие байты контейнера. Локальная обратная проверка целевой
|
||||
замены `НоваяСтрока.Выбран = Истина;` на более длинный фрагмент дала размер
|
||||
потока `36101 → 36198`, новый заголовок `36198/36198`, одно новое вхождение и
|
||||
нулевое старое. Все байты до заголовка выбранного потока сохранились.
|
||||
|
||||
Правило выбора кодека: fixed-width применяется **только** если доказан
|
||||
ненулевой непрозрачный хвост; если `declared_1 == declared_2` и хвоста нет,
|
||||
безопасна контролируемая замена переменной длины через структурный writer.
|
||||
Неизвестный или частично декодированный контейнер остаётся заблокированным,
|
||||
а не переводится в переменную длину по предположению.
|
||||
|
||||
## Правило публичного маршрута
|
||||
|
||||
Если объектный модуль состоит только из комментариев, это всё равно BSL-модуль:
|
||||
у него нет маркеров `Процедура`/`Функция`, но его наличие подтверждает
|
||||
структурный UTF-8-префикс в потоке. Адаптер обязан вернуть владельца и точный
|
||||
селектор чтения, не заставляя клиента искать поток. При записи он обязан
|
||||
использовать только fixed-width кодек, а не общий stream writer, который
|
||||
перезаписывает непрозрачный хвост. Парное обновление `__configinfo` допустимо
|
||||
только в отдельно подтверждённом каноническом layout.
|
||||
|
||||
## Повтор `code.write` после успешной записи
|
||||
|
||||
Повтор одного и того же публичного `code.write` не является новой операцией.
|
||||
До автоматической подготовки `ConfigCASSave` адаптер читает указанную
|
||||
процедуру в `effective_working`. Если старого фрагмента уже нет, а точный
|
||||
новый фрагмент присутствует ровно один раз в этой же процедуре, результат —
|
||||
`status: already_applied`, `applied: false`. В этом случае запрещены и
|
||||
подготовка saved-state, и новая SQL-запись.
|
||||
|
||||
Это правило предотвращает опасный путь: повторный запрос нельзя начинать с
|
||||
активного `ConfigCAS`, потому что его копирование способно заново построить
|
||||
рабочую копию из доизменённого источника и скрыть факт уже выполненной
|
||||
операции. Если оба фрагмента отсутствуют, новый фрагмент встречается
|
||||
несколько раз либо процедура не подтверждена, идемпотентность не
|
||||
предполагается: применяется обычная безопасная ошибка `not_found`/
|
||||
`ambiguous` или диагностика маршрута.
|
||||
|
||||
## Цепочка версий `2 → 3 → 4`
|
||||
|
||||
Третий live-SQL образец подтвердил повторяемую часть протокола. 20-байтовое
|
||||
Base64-поле в каждой новой версии равно SHA-1 сырого файла предыдущей версии:
|
||||
запись `3` хранит SHA-1 записи `2`, а запись `4` — SHA-1 записи `3`. Это
|
||||
доказанная ссылка версии, а не случайный текст. Его контрольный SHA-1:
|
||||
`fc84f0a9ef17034f8d82f44c5f9b07064864b524`.
|
||||
|
||||
Рядом расположен 16-байтовый токен, который меняется при каждом сохранении и
|
||||
дублируется фрагментами в трёх служебных местах. Алгоритм его создания не
|
||||
декодирован: адаптер его не генерирует и не изменяет. Его нельзя считать
|
||||
основанием для создания или изменения `__configinfo` в hash-keyed overlay.
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"schema": "onec_report_module_stream_sample.v1",
|
||||
"origin": "Configurator manual save in upo_test/test2/tt_Отчет; no adapter write",
|
||||
"logical_change": "// protocol-report-manual-change-2",
|
||||
"physical_file_name": "<extension-guid>__<report-guid>.0",
|
||||
"compression": "raw_deflate",
|
||||
"raw_sha1": "89827a3c7fa07ae50a268d20a7cee34195d1d7a7",
|
||||
"raw_bytes": 618,
|
||||
"inflated_sha1": "6385a80972841eb8a97df43596ed15dd16966495",
|
||||
"inflated_bytes": 1283,
|
||||
"raw_base64": "7VLJbhNBEG2iPlnyBXHgBKMRByDTcc/S0zNCBtnEie2MvI2XwQJFPat3J/ZkV4TCgV/gCge+ggMfwE9w4MaBTzDtbLIFQYgzT+rq6q5+9apVNZvNXoMVAG6BCyQT+ByyIVw7NLyAkExs8idv+Zpx2kdO+7Zy4V/RFCxcOws0of/q4cvcCnj/+OzRfJ+jC0YgBOMlVRwKl+wl+o9Pn09USZawJIoSPrUyL5qqOQ39SafeLxxF5YlbpLvtDc9JJhMZfdUYD+L8gdHqWa2CWqNuX8aHekQ766GjlUqpIKodbzQcJ5M+lZKJE1MSZeK6MnMZ0onMkKYGKnJVipFHNdPHpoI9F4tSs1jp9OS6TTZ9u0GmTlVtDtrtmm9YuTTPYRjEoExHGtVVpOnYQKYcYkRNrCvU1TSdeaJU2S8PGo2jdaVXtVvTeF1TUsNi02rtVKp/mWONV7JVydojpRo6qtVNPW8acbe+29JLBWNvnoXyqjXKufwj/Dcm0RHzVA/JpumFLlYJ8xVROp760f6w5tml4n6l1GnQYSNbdFyl0MN/yHHd56vxUJbG46q/i31+AGwQgwkIAANDflrst2bc2G9dkgMiK4y5SP6H6Yq53iG3S2raTWqplLAzGcdjbzxAk2BnPImRy6bBoDsK5uq/CQ/ZaI8NkNdhoyhACtY9X2WUiFLZjywrzvhReJDP94ZRJqjn28pqPlcz0qdwjZcUg21wD2ZgGj6FCrcYlqHA/SfQgoIgfN2y30l3efzZ5R09f4dhlvuLvC1ILnkGvMNtGd7mUQJz8P4vTMLPBJrwe/YNl/jwxT4TBPAf5/gJ"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"schema": "onec_report_module_stream_sample.v1",
|
||||
"origin": "Configurator manual save in upo_test/test2/tt_Отчет; no adapter write",
|
||||
"logical_change": "// protocol-report-manual-change-3",
|
||||
"physical_file_name": "<extension-guid>__<report-guid>.0",
|
||||
"compression": "raw_deflate",
|
||||
"raw_sha1": "357c39a55e8f66c3efd8f7aac968a4db5d5eb5fb",
|
||||
"raw_bytes": 620,
|
||||
"inflated_sha1": "eb72cadfe12f68969cd278901a6f83e627ee1a53",
|
||||
"inflated_bytes": 1283,
|
||||
"raw_base64": "7VLLbhMxFDWVV5GyQSxYwWjEApRxa8+MxzOqAkrV0HQ6ah6TVyMQ8jyTNMmkybSlVBWCBb8AWxZ8BQs+gBV/wIIdCz4huE8lgiLEmiP5+trX555r3TubzV6CJQBugHNkM/gMxJSuHBadQ8pmNsSTN2LNBO2DoH1bOvcvaSqWrpw5mrT79P6T4hKofHn34HQ/RQ+MQASSBVUcSRfsBfqPj5+ONYUoWJFlBZ84hZ2mZk2jYNKt724exeWJZ7O9zmO/nc1mCkbOTAZp6dBs9Z3WplZj3i6h+lGQs8l2vZ0YNNnTUr5hr0/zJ0o2c2wpMqGeR7jHkUEJR7oWasjTGEY+060AWyr2PSwrTbvS7ZO6SzcCt0Gn7arWHHQ6tcB0inmRwzSpybiBdGZoSDewiSwSYcQsbKjM03WD+7JSOSgPGo2jdbVfdVvTdF1XV4Z202mNK9W/zLEsKunt2EallIvD/caWneB4bLJ+tdOuEcMXWZioWmeCKz4ifmNRA3Ff8xGxLD/ysEZ5oMrKi2kQHwxrvrttH1S2uw02bKzZbU/d7OM/5Ljq8+V4qAvjcdnf+T7fAy5IwQSEgIOhOM33Wzev7behkJASlXMPkX+YrlToPRd2QU2/Tm1lRRpPkjTxkwGahONkkiKPT8NBbxSeqv8mPOSjfT5AfpeP4hBp2PADjTMqK+Ugdpy0EMTRYanUH8aFsF7qqLlSsWbmT+CyKCkFz8AdWIB5+BCqwmJYhpLwV6EDJUn6uuW+VW6L+KOLO3b2DsM14c/ztiC94JnwlrBleFNEKSzCu78wqThTaMHva6+FxPvP7itJAv9xhp8="
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# Configurator refresh behaviour
|
||||
|
||||
## Observed session model
|
||||
|
||||
`ConfigCASSave` is an SQL overlay, but writing it outside Configurator does not
|
||||
set Configurator’s in-memory changed/dirty state.
|
||||
|
||||
Observed consequences:
|
||||
|
||||
| State before adapter write | Minimal human action after write |
|
||||
|---|---|
|
||||
| Object already existed in saved-state | Close and reopen that object |
|
||||
| Adapter created the first pending object for an extension | Close and reopen the extension |
|
||||
| Adapter created the first pending object for base configuration | Close and reopen the configuration |
|
||||
|
||||
If the user manually edits and saves any object in an extension, Configurator
|
||||
marks the extension changed; reopening another object can then load its
|
||||
`ConfigCASSave` overlay.
|
||||
|
||||
## Required adapter response
|
||||
|
||||
Write results should return machine-readable guidance:
|
||||
|
||||
```json
|
||||
{
|
||||
"configurator_refresh": {
|
||||
"required": true,
|
||||
"scope": "object|extension|configuration",
|
||||
"action": "close_reopen_object|close_reopen_extension|close_reopen_configuration"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This guidance does not claim that the adapter controls Configurator; it merely
|
||||
reports the minimum observed reload boundary.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Known dead ends and prohibited shortcuts
|
||||
|
||||
## Do not repeat
|
||||
|
||||
- Do not write only a module `.0` row: `__configinfo` then references stale
|
||||
bytes and activation fails.
|
||||
- Do not use random, zeroed, copied-from-unrelated, or guessed HashVersion
|
||||
service atoms. A controlled random test produced a licensing/crypto error.
|
||||
- Do not write `Config` or `ConfigCAS` directly, even in tests.
|
||||
- Do not use a suffix such as `.0` as proof of “object module”.
|
||||
- Do not infer an object name from a GUI tree label, table suffix, or an
|
||||
approximate Russian name.
|
||||
- Do not overwrite all saved-state rows when adding a delta: preserve user
|
||||
work in other objects and every map entry.
|
||||
- Do not treat an open Configurator screen as SQL evidence.
|
||||
|
||||
## Open questions
|
||||
|
||||
- The generation algorithm and ownership of the 32-byte `__configinfo`
|
||||
service atom are unknown. Preservation is proven for the studied flow;
|
||||
generation is not.
|
||||
- Multi-part storage streams need a dedicated row-layout codec.
|
||||
- The extension protocol must be reproduced on another object class and a
|
||||
second platform build before being declared generally supported.
|
||||
- Configurator’s in-memory dirty-state implementation is inferred from
|
||||
behaviour, not decoded from platform source. Only the reload guidance is
|
||||
operationally relied upon.
|
||||
@@ -0,0 +1,51 @@
|
||||
# Contract for an agent using the 1C SQL adapter
|
||||
|
||||
The agent is a consumer of evidence returned by the adapter. It must never
|
||||
turn a plausible interpretation into a fact.
|
||||
|
||||
## Fundamental adapter rule
|
||||
|
||||
The adapter is a **SQL codec**, not an expert system for 1C. It reads and
|
||||
writes only according to the versioned configuration-storage specification
|
||||
that has been decoded from live SQL and recorded in this knowledge base.
|
||||
It may expose a semantic name only when that mapping is proven by the decoder.
|
||||
For an unknown carrier, field, child object, byte range, checksum, or service
|
||||
atom, the only valid result is `unsupported`, `partial`, or `ambiguous` with
|
||||
the observed evidence. It must not synthesize a structure, BSL, or value to
|
||||
make an operation appear complete.
|
||||
|
||||
## Required behaviour
|
||||
|
||||
- Start from a public 1C name/ref supplied by the user.
|
||||
- Ask the adapter to resolve the live route; internal GUIDs, SQL numbers and
|
||||
file names remain adapter implementation details.
|
||||
- Use public `code.read`, `code.search`, and `code.write` for normal BSL work.
|
||||
Never pass or request `ConfigCAS`, `ConfigCASSave`, a payload hash, a
|
||||
canonical saved filename, or a stream index. Those are diagnostic evidence,
|
||||
not an agent-facing selector contract.
|
||||
- Read the target bytes before proposing any edit.
|
||||
- Quote the exact proven fragment, its count, and the selected layer.
|
||||
- For a write, require a plan/preflight and retain the returned rollback and
|
||||
refresh guidance.
|
||||
- State `unknown`, `ambiguous`, or `protocol_incomplete` when evidence is
|
||||
absent. Ask for a larger fragment or a human Configurator action instead of
|
||||
guessing.
|
||||
|
||||
## Forbidden behaviour
|
||||
|
||||
- Invent BSL procedures, form controls, field paths, joins, storage tables,
|
||||
module streams, extension ownership, or `__configinfo` atoms.
|
||||
- Claim that a SQL saved-state edit is active before active-layer verification.
|
||||
- Claim a repository lock merely because an adapter request was recorded.
|
||||
- Tell the user that the Configurator UI has refreshed unless the required
|
||||
close/reopen boundary was completed by the human.
|
||||
- Write directly to active configuration or application tables.
|
||||
- Work around a public-route failure by retrying against an internal module
|
||||
reference. Report the public `not_found`, `ambiguous`, `unsupported`, or
|
||||
`protocol_incomplete` result so the adapter can be corrected.
|
||||
|
||||
## Write-result language
|
||||
|
||||
Use the adapter’s `configurator_refresh` object verbatim in human-facing
|
||||
instructions. Do not collapse `object`, `extension`, and `configuration` into
|
||||
the same generic “restart” advice.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Evidence and safety rules
|
||||
|
||||
## Evidence levels
|
||||
|
||||
| Level | Meaning | Permitted use |
|
||||
|---|---|---|
|
||||
| Observed | Read from live SQL once | Diagnosis only |
|
||||
| Reproduced | Seen in independent before/after saves | Decoder rule candidate |
|
||||
| Round-trip proven | Decode → unchanged encode returns identical bytes | Safe read/transform component |
|
||||
| Activation proven | A human Configurator applied it and active SQL verified it | Controlled `upo_test` writer component |
|
||||
|
||||
No rule may be promoted because a name, suffix, or payload shape “looks right”.
|
||||
|
||||
## Codec boundary
|
||||
|
||||
The configuration-storage specification is the adapter's sole authority for
|
||||
decoding and encoding. A writer is enabled only when the relevant version of
|
||||
that specification defines every changed byte and every dependent integrity
|
||||
atom, and round-trip evidence proves the encoder. Any remaining opaque atom is
|
||||
preserved byte-for-byte; if a requested edit requires changing it, the write is
|
||||
unsupported until the specification is extended by a controlled experiment.
|
||||
|
||||
## Allowed mutation boundary
|
||||
|
||||
- The adapter runtime uses SQL only.
|
||||
- `upo_test` may receive controlled writes to `ConfigSave` and
|
||||
`ConfigCASSave` only.
|
||||
- `Config`, `ConfigCAS`, and application data are never direct write targets.
|
||||
- A write must have a live target resolution, optimistic SHA-1 precondition,
|
||||
reversible evidence, atomic transaction, and readback verification.
|
||||
- Repository coordination is a separate policy; an adapter marker is not a
|
||||
native repository lock.
|
||||
|
||||
## Exact edits
|
||||
|
||||
For BSL fragment replacement, provide one of:
|
||||
|
||||
- an old fragment occurring exactly once;
|
||||
- a larger unique surrounding fragment;
|
||||
- a proven structural container path/offset plus original SHA-1.
|
||||
|
||||
If a fragment occurs zero or more than once, do not choose arbitrarily. Return
|
||||
the count and candidate contexts. Deletion follows the same rule.
|
||||
|
||||
## What an agent must report
|
||||
|
||||
Every proposal and applied result must state:
|
||||
|
||||
- active and saved layers used;
|
||||
- public target and SQL evidence retained internally;
|
||||
- original and new SHA-1 values;
|
||||
- paired files changed;
|
||||
- exact-match count;
|
||||
- rollback reference;
|
||||
- Configurator refresh guidance.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Protocol experiment record template
|
||||
|
||||
Create one file per material experiment under `experiments/`.
|
||||
|
||||
```markdown
|
||||
# <date> — <short target and action>
|
||||
|
||||
## Scope
|
||||
- Base and classification:
|
||||
- Extension/object public names:
|
||||
- Runtime/platform build:
|
||||
- Authority for mutation:
|
||||
|
||||
## Before
|
||||
- Active source table/key/SHA-1:
|
||||
- Saved-state files and SHA-1:
|
||||
- Relevant descriptor/map entries:
|
||||
|
||||
## Exact action
|
||||
- Public target resolution evidence:
|
||||
- Old fragment / structural selector and occurrence count:
|
||||
- Payload codec and round-trip result:
|
||||
- Paired files written in one transaction:
|
||||
|
||||
## After SQL evidence
|
||||
- Readback SHA-1 values:
|
||||
- Map/reference validation:
|
||||
- Unrelated pending files preserved:
|
||||
|
||||
## Human Configurator verification
|
||||
- Reload action:
|
||||
- Visible result:
|
||||
- Apply result:
|
||||
- Active/saved postcondition:
|
||||
|
||||
## Outcome
|
||||
- Proven fact:
|
||||
- Failed hypothesis:
|
||||
- Remaining unknown:
|
||||
- Follow-up regression fixture/test:
|
||||
```
|
||||
@@ -0,0 +1,121 @@
|
||||
# 1C Adapter: universal write dispatcher and typed handlers
|
||||
|
||||
## Decision
|
||||
|
||||
The public write contract stays universal and name-first:
|
||||
|
||||
```text
|
||||
code.write / metadata.write
|
||||
-> resolve public object and extension layer
|
||||
-> plan and gates
|
||||
-> select one typed handler
|
||||
-> prepare saved state internally when required
|
||||
-> apply, SQL-readback, rollback evidence
|
||||
```
|
||||
|
||||
The caller never selects an SQL table, saved file, stream, payload codec, or
|
||||
handler. If no handler has a proven capability for the requested object type
|
||||
and operation, the dispatcher returns `unsupported_write_target` with a public
|
||||
explanation. It must not fall back to a generic byte rewrite.
|
||||
|
||||
## Current state
|
||||
|
||||
The behaviour is already logically separated, but is physically concentrated
|
||||
in `plugins/1c/connector/adapter_1c_server.py` (about 3.5 MB). The main
|
||||
dispatcher is `metadata_write` and currently branches to:
|
||||
|
||||
| Public target | Existing internal writer |
|
||||
|---|---|
|
||||
| BSL module | `metadata_module_write_apply` |
|
||||
| BSL embedded in a managed form | `form_embedded_module_handler_write_apply` |
|
||||
| Form element/property | `metadata_form_element_write_apply` |
|
||||
| Form command/button caption | `metadata_form_command_button_write` |
|
||||
| Scalar object/member property | `metadata_object_property_write` |
|
||||
| Add object member | `metadata_object_member_add` |
|
||||
| Scheduled-job schedule | `metadata_scheduled_job_schedule_write` |
|
||||
|
||||
This is a suitable functional base. The problem is coupling: routing,
|
||||
saved-state preparation, result shaping, codecs, SQL writes, and HTTP/RPC
|
||||
dispatch live in one module, so a change in one type is too likely to affect
|
||||
another.
|
||||
|
||||
## Target module layout
|
||||
|
||||
```text
|
||||
plugins/1c/connector/
|
||||
adapter_1c_server.py # HTTP, RPC registration, composition root only
|
||||
write/
|
||||
contracts.py # WriteIntent, WritePlan, WriteResult, capability errors
|
||||
dispatcher.py # universal metadata.write dispatch; no SQL codecs
|
||||
gates.py # layer, repository, optimistic-hash and mode gates
|
||||
saved_state.py # Config→ConfigSave / ConfigCAS→ConfigCASSave prepare + receipt rollback
|
||||
registry.py # handler registration and deterministic selection
|
||||
handlers/
|
||||
module.py
|
||||
embedded_form_module.py
|
||||
form_element.py
|
||||
form_command.py
|
||||
object_property.py
|
||||
object_member.py
|
||||
scheduled_job.py
|
||||
unsupported.py
|
||||
storage/
|
||||
sql_saved_state.py # transactions, guarded row copy, backup/readback
|
||||
extension_routes.py # active-to-saved route and cache refresh
|
||||
```
|
||||
|
||||
`parser/` remains the place for pure payload decoding/encoding. A handler may
|
||||
use a parser codec only where its round-trip proof exists; SQL access is
|
||||
provided through a narrow context rather than imported globals.
|
||||
|
||||
## Handler contract
|
||||
|
||||
Each handler implements the same four operations:
|
||||
|
||||
1. `can_handle(intent, evidence) -> supported | unsupported | ambiguous`.
|
||||
2. `plan(intent, evidence) -> WritePlan` with exact guards and no mutation.
|
||||
3. `apply(plan, context) -> WriteResult` only after shared gates succeed.
|
||||
4. `rollback(result, context)` when the handler created reversible state.
|
||||
|
||||
The dispatcher selects exactly one handler. Zero handlers yields
|
||||
`unsupported_write_target`; multiple handlers yield `ambiguous_write_handler`.
|
||||
Handlers never select another extension layer after dispatch.
|
||||
|
||||
`metadata.write.capabilities` also returns `registered_handlers`. This makes
|
||||
the runtime registry visible beside the broader capability matrix and prevents
|
||||
an API claim from silently drifting away from the installed handlers.
|
||||
|
||||
## Invariants owned centrally
|
||||
|
||||
- public name/ref and extension scope resolve before handler selection;
|
||||
- only saved layers are writable;
|
||||
- saved-state preparation is internal and idempotent;
|
||||
- optimistic hash, audit event, backup/receipt, SQL readback and rollback
|
||||
policy are common infrastructure;
|
||||
- SQL readback is not described as Configurator activation;
|
||||
- low-level storage fields are redacted from name-first responses.
|
||||
|
||||
## Safe migration order
|
||||
|
||||
1. **Completed:** add storage-free `write/contracts.py`, typed handler
|
||||
declarations under `write/handlers/`, and `write/registry.py`; connect
|
||||
`metadata.write` to the registry while delegating to existing writers
|
||||
unchanged. The registry has a deny-by-default result for unknown target
|
||||
kinds.
|
||||
2. **In progress:** add `write/context.py`; the scheduled-job route now enters
|
||||
its typed handler through this explicit context. The handler still delegates
|
||||
to the single existing implementation until its body moves in one change.
|
||||
3. Move `saved_state.py` and `storage/sql_saved_state.py` first. The recently
|
||||
proven hash-keyed extension prepare/rollback smoke is its acceptance test.
|
||||
4. Extract the least coupled handlers: scheduled job, object property, object
|
||||
member.
|
||||
5. Extract form element and form command handlers.
|
||||
6. Extract module and embedded-form-module handlers last; retain their exact
|
||||
payload codec and paired-write tests.
|
||||
7. Reduce `metadata_write` to validation, plan/gate orchestration and one
|
||||
registry call. Keep the old public API names and response schema intact.
|
||||
|
||||
Do not split by copying code into parallel paths. Each extraction must move one
|
||||
authoritative implementation, keep the existing tests green, and add one
|
||||
handler-level `plan → apply_and_rollback → readback` test in `upo_test` when a
|
||||
matching fixture exists.
|
||||
@@ -0,0 +1,224 @@
|
||||
# Аналитика адаптера 1С: передача проекта
|
||||
|
||||
Дата актуализации: 2026-08-07.
|
||||
|
||||
## Назначение
|
||||
|
||||
`adapter-observer` — независимый read-only веб-интерфейс аналитики для SQL-only адаптера 1С. Он читает журналы REST и MCP, получает разрешённые снимки метаданных через публичный API адаптера и не имеет прямого доступа к SQL-базе 1С.
|
||||
|
||||
Сервис не является зависимостью `adapter-1c-rest` или `adapter-1c-mcp`: остановка либо обновление observer не должна влиять на работу адаптера.
|
||||
|
||||
## Описание адаптера 1С
|
||||
|
||||
Адаптер 1С — SQL-only сервис для чтения метаданных и выполнения строго контролируемых операций с saved-state конфигурации 1С. Он работает через подтверждённые структуры SQL-хранилища; не запускает Configurator и не должен выдумывать метаданные, маршруты или двоичные payload.
|
||||
|
||||
Основные части:
|
||||
|
||||
- `adapter-1c-rest` — REST API адаптера;
|
||||
- `adapter-1c-mcp` — MCP-шлюз, который вызывает REST API;
|
||||
- `adapter-1c-audit` — аудит-контур REST;
|
||||
- `adapter-observer` — независимая аналитика журналов и разрешённых read-only вызовов.
|
||||
|
||||
Код адаптера находится в текущем репозитории:
|
||||
|
||||
```text
|
||||
plugins/1c/connector/adapter_1c_server.py реализация REST/RPC методов
|
||||
plugins/1c/connector/contracts/openapi.yaml публичный HTTP-контракт
|
||||
plugins/1c/mcp/adapter_1c_mcp.py MCP-шлюз
|
||||
plugins/1c/observer/ аналитика адаптера
|
||||
```
|
||||
|
||||
### Где развёрнут адаптер
|
||||
|
||||
| Контур | REST | MCP | Docker-хост | Назначение |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Production / внешний | `http://docker.cin.su:8011` | `http://docker.cin.su:8021/mcp` | `docker.cin.su` | Основной внешний адаптер и observer. |
|
||||
| Staging / тестовый | `http://docker-test.cin.su:8011` при наличии тестового стека | `http://docker-test.cin.su:8021/mcp` при наличии тестового стека | `test-docker` | Проверка перед production. |
|
||||
| Изолированная тестовая база | `base_id=upo_test` | через соответствующий MCP | выбранный контур | Разрешены контролируемые тесты и rollback. |
|
||||
|
||||
Production-контейнеры на `docker.cin.su`:
|
||||
|
||||
```text
|
||||
adapter-1c-rest порт 8011
|
||||
adapter-1c-mcp порт 8021
|
||||
adapter-1c-audit внутренний аудит REST
|
||||
adapter-observer порт 8031
|
||||
```
|
||||
|
||||
## Как проверять новые функции адаптера
|
||||
|
||||
### До развёртывания
|
||||
|
||||
1. Изменить реализацию в `plugins/1c/connector/adapter_1c_server.py` и зафиксировать публичный контракт в `plugins/1c/connector/contracts/openapi.yaml`.
|
||||
2. Добавить либо обновить unit/smoke-тест в `tests/1c/` или `scripts/smoke_1c_*.py`.
|
||||
3. Для новой метадаты или SQL-маршрута сначала получить доказательства из live SQL в `upo_test`; при неполном codec вернуть `unsupported`/`protocol_incomplete`, а не предполагать данные.
|
||||
4. Прогнать тесты и smoke-проверку на `upo_test`.
|
||||
|
||||
### Staging-проверка
|
||||
|
||||
Развернуть обновлённые `adapter-1c-rest` и при необходимости `adapter-1c-mcp` на `test-docker`. Не использовать staging вместо external MCP production без явного запроса.
|
||||
|
||||
Проверять новый read-only метод через REST/RPC с явным `base_id=upo_test`. Проверка write-маршрута должна пройти обязательные plan/preflight/apply/rollback-gates и не даёт права заявлять, что Configurator принял или активировал изменение.
|
||||
|
||||
Полезные проверки:
|
||||
|
||||
```powershell
|
||||
# Контейнеры и порты выбранного контура
|
||||
docker --host ssh://test-docker ps --format '{{.Names}} {{.Image}} {{.Status}} {{.Ports}}'
|
||||
|
||||
# Контракт/доступные методы на REST
|
||||
Invoke-WebRequest -UseBasicParsing http://docker-test.cin.su:8011/methods
|
||||
|
||||
# Health observer после тестового вызова
|
||||
Invoke-WebRequest -UseBasicParsing http://docker-test.cin.su:8031/health
|
||||
```
|
||||
|
||||
Пути и аргументы нового метода нельзя составлять по догадке: использовать только его документированный контракт и подтверждённые публичные селекторы.
|
||||
|
||||
## Как обновлять аналитику вместе с адаптером
|
||||
|
||||
Каждое изменение адаптера нужно оценивать как изменение наблюдаемого контракта.
|
||||
|
||||
| Изменение адаптера | Что изменить в observer |
|
||||
| --- | --- |
|
||||
| Новый метод аудита или новый статус | Проверить `event_view`, фильтр статусов, группировку summary и русские подписи. |
|
||||
| Новый read-only метод для объекта | Добавить его в жёсткий allowlist `/api/object/action` только после проверки публичного селектора и безопасного ответа. |
|
||||
| Новый тип метаданных | Добавить его в `treeGroups`, если он должен быть виден в дереве. |
|
||||
| Новое поле длительности | Оставить в API машинское значение, а в UI провести через `duration()`. |
|
||||
| Изменение схемы audit JSONL | Сохранить обратную совместимость: неизвестные поля показывать только в деталях, отсутствующие поля считать необязательными. |
|
||||
| Новый write-маршрут | Не добавлять кнопку выполнения в observer. Допустимо отобразить только подтверждённую capability/статус после отдельного проектного решения. |
|
||||
|
||||
Обязательная последовательность релиза:
|
||||
|
||||
1. Сначала обновить адаптер и проверить его новый метод на `upo_test`.
|
||||
2. Убедиться, что REST/MCP audit содержит безопасную запись вызова без SQL, BSL, payload и секретов.
|
||||
3. Обновить observer в staging; открыть новый сценарий в UI и проверить, что метод не классифицируется как `exception` ошибочно.
|
||||
4. Обновить observer на production вместе с совместимой версией адаптера.
|
||||
5. Проверить `/health`, журнал, аналитику и конкретный объект в дереве.
|
||||
|
||||
Observer не должен требовать одновременный рестарт адаптера. При выпуске только frontend/observer достаточно пересоздать `adapter-observer`; REST и MCP остаются запущенными.
|
||||
|
||||
## Что сделано
|
||||
|
||||
### Интерфейс
|
||||
|
||||
- Журнал REST-запросов с фильтрами по методу, базе, статусу, периоду и минимальной длительности.
|
||||
- Аналитика p50/p95, медленных методов, исключений и ожидаемых безопасных отказов.
|
||||
- Корреляция MCP ↔ REST по `request_id`.
|
||||
- Дерево метаданных конфигурации с разделом «Справочники».
|
||||
- Для каждого доступного справочника отображаются read-only действия:
|
||||
- Карточка;
|
||||
- Свойства;
|
||||
- Реквизиты;
|
||||
- Формы;
|
||||
- Команды;
|
||||
- Модули;
|
||||
- Макеты;
|
||||
- Связи.
|
||||
- Результат действия открывается в диалоге с названием операции, объектом, статусом и длительностью.
|
||||
- Поиск по уже загруженному списку справочников и счётчик `Показано: N из M`.
|
||||
|
||||
### Время выполнения
|
||||
|
||||
- Во всех пользовательских представлениях миллисекунды форматируются в секунды, минуты и часы.
|
||||
- Фильтр минимальной длительности вводится в секундах.
|
||||
- В технических API-полях сохраняется `duration_ms`: это контрактное машинное значение, не пользовательская подпись.
|
||||
|
||||
### Производительность
|
||||
|
||||
- Observer отдаёт до 1000 объектов за один запрос к `metadata.objects.list`.
|
||||
- Для базы `upo` загружается 798 доступных справочников из 825 объектов одного типа одним запросом; 27 объектов скрыты адаптером как отсутствующие/нечитаемые.
|
||||
- Проверенное время live-сканирования этого списка: около 18,7 секунды. Это время адаптера и SQL-чтения, а не рендеринга кнопок в браузере.
|
||||
|
||||
### Безопасность
|
||||
|
||||
- Observer вызывает только жёстко заданный allowlist read-only методов для строки справочника.
|
||||
- Новые действия не выполняют запись, подготовку saved-state, активацию конфигурации или операции Configurator.
|
||||
- В интерфейсе не восстанавливаются исторические запросы из audit JSONL.
|
||||
|
||||
## Что ещё нужно сделать
|
||||
|
||||
Приоритетный следующий этап:
|
||||
|
||||
1. Добавить быстрый серверный поиск справочника по имени, чтобы не ожидать полное live-сканирование при работе с одним объектом.
|
||||
2. Добавить отображение прогресса при загрузке больших разделов: число прочитанных объектов, текущая страница и время ожидания.
|
||||
3. Вынести перечень разрешённых действий и русские названия в отдельную конфигурацию/контракт, а не хранить в фронтенд-коде.
|
||||
4. Добавить компактные пользовательские карточки результатов действий вместо показа полного JSON; JSON сохранить как диагностическую вкладку.
|
||||
5. Добавить тесты UI/HTTP для сценария: открыть «Справочники» → загрузить → увидеть кнопки → выполнить «Карточка».
|
||||
6. Добавить version/release marker в `/health` и UI, чтобы быстро отличать старую Docker-сборку от актуальной.
|
||||
7. Добавить снимки и сравнение аналитики между релизами: список методов, покрытие метаданных, p50/p95 и изменения ошибок.
|
||||
8. До публикации вне внутренней сети определить аутентификацию, роли, срок хранения audit-данных и экспортируемые поля.
|
||||
|
||||
Не реализовывать без отдельного разрешения:
|
||||
|
||||
- повтор исторических write/activation/repository-запросов;
|
||||
- прямое подключение observer к SQL 1С;
|
||||
- запуск или автоматизацию Configurator;
|
||||
- вывод BSL-текста, SQL-полей, паролей или ключей из журналов.
|
||||
|
||||
## Исходные файлы
|
||||
|
||||
```text
|
||||
plugins/1c/observer/
|
||||
observer_server.py HTTP API и безопасный allowlist действий
|
||||
web/index.html оболочка интерфейса
|
||||
web/assets/app.js UI, форматирование времени, дерево, действия
|
||||
web/assets/style.css стили
|
||||
Dockerfile образ observer
|
||||
|
||||
core/deploy/docker/adapter-observer/
|
||||
compose.yaml отдельный Docker Compose стек
|
||||
.env.example пример runtime-переменных
|
||||
|
||||
docs/runbooks/adapter-observer.md
|
||||
эксплуатационный контракт и ограничения
|
||||
```
|
||||
|
||||
## Docker-развёртывание
|
||||
|
||||
### Текущий production-хост
|
||||
|
||||
- Docker host: `docker.cin.su`.
|
||||
- Контейнер: `adapter-observer`.
|
||||
- URL: `http://docker.cin.su:8031/`.
|
||||
- Образ: `adapter-observer:latest`.
|
||||
- Порт: `8031`.
|
||||
- Внешняя сеть адаптера: `adapter-1c_default`.
|
||||
- Read-only тома журналов:
|
||||
- `adapter-1c_adapter-1c-data` → `/audit:ro`;
|
||||
- `adapter-1c-mcp_adapter-1c-mcp-data` → `/mcp-audit:ro`.
|
||||
- Собственный state-том: `adapter-observer_adapter-observer-state`.
|
||||
|
||||
### Команда обновления
|
||||
|
||||
Из корня текущего репозитория:
|
||||
|
||||
```powershell
|
||||
$env:DOCKER_HOST = 'ssh://docker.cin.su'
|
||||
docker compose `
|
||||
--project-directory 'Z:\codex\LLM\core\deploy\docker\adapter-observer' `
|
||||
-f 'Z:\codex\LLM\core\deploy\docker\adapter-observer\compose.yaml' `
|
||||
up -d --build adapter-observer
|
||||
```
|
||||
|
||||
После обновления:
|
||||
|
||||
```powershell
|
||||
Invoke-WebRequest -UseBasicParsing http://docker.cin.su:8031/health
|
||||
docker --host ssh://docker.cin.su ps --filter 'name=^/adapter-observer$'
|
||||
```
|
||||
|
||||
Команда пересоздаёт только `adapter-observer`. Не запускать `docker compose down` в проектах REST/MCP адаптера и не перезапускать `adapter-1c-rest` или `adapter-1c-mcp` ради обновления аналитики.
|
||||
|
||||
### Тестовый хост
|
||||
|
||||
Для staging используется тот же стек с `DOCKER_HOST='ssh://test-docker'` и URL `http://docker-test.cin.su:8031/`.
|
||||
|
||||
## Проверки после переноса
|
||||
|
||||
1. `GET /health` возвращает `status: ok` и показывает файлы REST/MCP audit.
|
||||
2. Открыть «Дерево объектов» и загрузить `upo`.
|
||||
3. Нажать «Читать» у «Справочники»: блок должен остаться раскрытым.
|
||||
4. Убедиться, что видна строка вида `798 объектов · N с` без единицы `мс`.
|
||||
5. У первой строки должны быть восемь кнопок действий.
|
||||
6. Нажать «Карточка»: открывается диалог с успешным статусом и читаемой длительностью.
|
||||
@@ -0,0 +1,54 @@
|
||||
# Проверка векторного поиска по коду 1С
|
||||
|
||||
Дата проверки: 2026-07-26. База: `upo_test`.
|
||||
|
||||
## Контур
|
||||
|
||||
- актуальный SQL-derived индекс адаптера: 1 080 модулей, 22 538 chunks;
|
||||
- очередь изменений: 0, snapshot чистый;
|
||||
- модель: `Qwen3-Embedding-0.6B-GGUF`, `Q8_0`;
|
||||
- runtime: `llama.cpp`, CPU-only на Ryzen 9 5900X;
|
||||
- endpoint: `http://docker-gpu.cin.su:8082/v1/embeddings`;
|
||||
- вектор модели: 1024, сохраняемый Matryoshka-срез: 384;
|
||||
- RTX 4090 сервисом эмбеддингов не используется.
|
||||
|
||||
## Измерения
|
||||
|
||||
Прогретый короткий запрос к endpoint: 0,03–0,08 с. Индексация 100 процедур и
|
||||
функций длиной до 1 000 символов заняла 54,52 с, ошибок и конфликтов нет.
|
||||
|
||||
Контрольные запросы к строгому поиску:
|
||||
|
||||
| Запрос | Top-1 | Время |
|
||||
|---|---|---:|
|
||||
| проверить является ли объект документом | `ЭтоДокумент` | 0,70 с |
|
||||
| преобразовать число из строки | `ЧислоИзСтроки` | 0,44 с |
|
||||
| добавить реквизит в HTML представление | `ДобавитьРеквизитКHTML` | 0,50 с |
|
||||
|
||||
Каждый результат был повторно проверен по live SQL. Чтение `read_selector`
|
||||
вернуло точное тело `ЭтоДокумент`, а не сохранённый текст из векторного кеша.
|
||||
|
||||
## Решение по отдельной векторной БД
|
||||
|
||||
Пока не добавлять. SQLite остаётся достаточным для текущего инкрементального
|
||||
контура и проще связывает `chunk_id`, `text_sha1`, outbox и проверку
|
||||
актуальности. Векторный кеш не является источником истины.
|
||||
|
||||
Повторно оценить ANN-хранилище после заполнения не менее 5 000 актуальных
|
||||
384-мерных vectors. Практические триггеры:
|
||||
|
||||
- p95 строгого поиска выше 1 секунды;
|
||||
- более 50 000 актуальных chunks;
|
||||
- SQLite-файл адаптера больше 1 ГиБ из-за embeddings.
|
||||
|
||||
Если триггер сработает, Qdrant/pgvector должен быть только производной копией:
|
||||
ключ `chunk_id`, обязательный `text_sha1`, namespace базы/модели/размерности,
|
||||
удаление через outbox. Перед ответом адаптер всё равно проверяет live SQL.
|
||||
|
||||
## Выявленный следующий приоритет
|
||||
|
||||
Глобально найденные активные модули пока часто имеют `object_ref=null`.
|
||||
Безопасный `read_selector` и имя процедуры присутствуют, но публичное имя
|
||||
владельца 1С не восстановлено. Это не проблема вектора; это неполный
|
||||
`metadata.module_owner_cache`. Следующая доработка — фоновое построение
|
||||
name-first карты владельцев и lazy backfill только для top-кандидатов.
|
||||
@@ -1,7 +1,8 @@
|
||||
# 1C Agent Coding Contract
|
||||
|
||||
This contract is the default rule set for coding agents that work through the
|
||||
1C adapter.
|
||||
1C adapter. The agent calls it only through the `onec_request` MCP tool; the
|
||||
REST SQL adapter is MCP's private downstream transport.
|
||||
|
||||
## Default View
|
||||
|
||||
@@ -13,6 +14,57 @@ This contract is the default rule set for coding agents that work through the
|
||||
- Objects can exist only in saved-state and can later be activated or canceled.
|
||||
Do not hide them from the agent view.
|
||||
|
||||
### Empty saved-state layer
|
||||
|
||||
An empty saved-state layer is normal before the first edit. In that case an
|
||||
agent still uses only public selectors and reports the adapter result; it must
|
||||
not obtain an active `ConfigCAS`/`Config` module reference and retry by hand.
|
||||
|
||||
Known adapter limitation (2026-08-02): for an extension-owned object module
|
||||
with no prepared saved-state row, public `code.read` can return
|
||||
`source_missing` even though the active module is proven to exist. Treat this
|
||||
as an adapter defect, not as evidence that the 1C object or BSL is absent.
|
||||
Use a developer-owned diagnostic check to investigate it; do not expose its
|
||||
storage coordinates to a coding agent.
|
||||
|
||||
## Required adapter acceptance fixture
|
||||
|
||||
`upo_test` must contain one isolated extension-owned `Report` with a decoded
|
||||
`object_module`, a unique BSL anchor, and no shared business role. This fixture
|
||||
is created and maintained by a human in Configurator; the SQL-only adapter must
|
||||
not fabricate it. It is the required target for the public acceptance sequence:
|
||||
|
||||
```text
|
||||
code.read(ref, extension, module_ordinal=1)
|
||||
→ code.write(old, new, apply_and_rollback)
|
||||
→ code.search verifies new text
|
||||
→ rollback and saved-state cleanup
|
||||
```
|
||||
|
||||
The request must contain no `layer`, table, file name, module ref, payload hash,
|
||||
or stream index. Until the fixture exists, unit tests prove routing only; they
|
||||
do not prove a live extension Report write.
|
||||
|
||||
### Verified base-module smoke
|
||||
|
||||
On 2026-08-02 the public sequence was verified on `upo_test` against base
|
||||
`Report.АвтоматическиеСкидки`, `module_ordinal=1`: a unique comment replacement
|
||||
completed in about 25 seconds with `verified_and_rolled_back`. The adapter
|
||||
auto-prepared `ConfigSave`, read back the write, rolled back the BSL change,
|
||||
then removed the exact rows it had prepared. The public result contains the
|
||||
opaque `prepare_receipt_id` and `prepare_cleanup`; the final saved-state status
|
||||
was `empty`. This validates the base route only, not the extension Report route.
|
||||
Live check on 2026-08-02 found 19 extensions but zero extension-owned Reports
|
||||
in `upo_test`; therefore the required extension acceptance fixture is currently
|
||||
missing and the extension write branch remains unaccepted.
|
||||
|
||||
Live check on 2026-08-13 found an extension BSL stream for
|
||||
`Report.УОП_ИнвентаризационнаяОпись`, but its `role_status=unconfirmed`.
|
||||
That is read-only evidence, not an acceptance fixture: do not write through an
|
||||
ordinal, storage reference, or guessed Configurator role. A human must add or
|
||||
identify one extension Report with a decoded `object_module` before the live
|
||||
extension `apply_and_rollback` test can run.
|
||||
|
||||
## Compare Views
|
||||
|
||||
- Use `state=both` or `source_state=all` only when the task needs a comparison
|
||||
@@ -33,6 +85,34 @@ Use public names and selectors:
|
||||
3. `code.read` with `state=working` to read the module or routine.
|
||||
4. `code.read` with `state=both` only for an explicit saved-vs-active check.
|
||||
|
||||
For a report form, keep the descriptions separate:
|
||||
|
||||
1. `metadata.object.forms(ref=Report.<name>, source_state=working)` lists the
|
||||
report's form references.
|
||||
2. `metadata.object.form.details` with that same public report ref and the
|
||||
returned form name reads the form description (attributes, parameters,
|
||||
commands, items, and the form module).
|
||||
3. `metadata.form.decode(view=structure)` is an optional compact static
|
||||
projection. It must retain `unresolved` parent/child links where no SQL
|
||||
codec has proved them.
|
||||
|
||||
A form command does not have its own module. Its handler is a named routine in
|
||||
the form module only when the decoder returns an evidenced command link.
|
||||
|
||||
Use the `read_selector.selector_token` returned by discovery with the method
|
||||
declared in that selector. Do not copy GUIDs, module refs, table names, or file
|
||||
names into a follow-up request. A module ordinal, display name, or storage-derived role
|
||||
is not a Configurator-tree path. If a result has `role_status=unconfirmed` or
|
||||
`configurator_path_status=unconfirmed`, the agent must not name, write, or
|
||||
infer its tree owner; report the missing decoder evidence instead.
|
||||
|
||||
When checking object commands, call `metadata.object.commands` with the same
|
||||
public `ref`/`kind`+`name` and `extension` selector. The adapter resolves the
|
||||
extension name and reads its active metadata itself. A prior `not_found` from a
|
||||
route that did not carry the extension context is not evidence that the report
|
||||
has no commands. Conversely, a BSL stream suffix alone is never evidence of a
|
||||
command or of its module path.
|
||||
|
||||
Agents should ask for and report object names, routine names, and code text.
|
||||
They should not ask users for SQL tables, storage file names, stream indexes, or
|
||||
saved-state write flags during normal coding work.
|
||||
@@ -52,12 +132,52 @@ Supported public edit shapes:
|
||||
say "save this code" and send the desired code text. It must not ask whether SQL
|
||||
saved-state apply flags are allowed.
|
||||
|
||||
For extension object modules, preparation maps the active payload route to its
|
||||
canonical saved-state filename internally. A public `code.write` therefore
|
||||
continues with `extension`, `ref`, module role, and the proven replacement
|
||||
only; it must never ask the caller to supply the canonical filename or stream.
|
||||
|
||||
Every successful `code.write` response must show:
|
||||
|
||||
- `write_mode.target=saved_state`;
|
||||
- `write_mode.activation_state=not_activated`;
|
||||
- `write_mode.production_apply=false`.
|
||||
|
||||
### Repository-controlled extension writes
|
||||
|
||||
For a repository-controlled extension, use the public sequence below and stop
|
||||
when it asks for a human Configurator action:
|
||||
|
||||
```text
|
||||
code.search(extension, ref, old fragment)
|
||||
→ repository.lock.plan(extension, ref)
|
||||
→ repository.lock.request
|
||||
→ human captures the exact object in Configurator
|
||||
→ repository.lock.confirm
|
||||
→ code.write(extension, ref, module_ordinal, old, new, repository_lock)
|
||||
→ code.search readback
|
||||
```
|
||||
|
||||
The lock plan for `upo / фс_Отчеты1 /
|
||||
Report.фс_ПродовольственнаяКорзина` resolves exactly one object:
|
||||
`Отчет.фс_ПродовольственнаяКорзина`, in extension layer
|
||||
`extension:8e02accd-8a34-11f1-8294-005056b0d483`. SQL cannot prove a native
|
||||
Configurator capture; only a human confirmation can continue this route.
|
||||
|
||||
`metadata.write.preflight` currently plans canonical high-level
|
||||
`metadata.write` targets and may return `needs_route` for this compatibility
|
||||
`code.write` shape before saved-state preparation. This is not permission to
|
||||
use its low-level payload hint. Use `code.write(mode=plan)` instead: a first
|
||||
extension edit may return public `needs_prepare`, which means to confirm the
|
||||
repository lock and reissue the same public request in an apply mode. Keep
|
||||
using the public `code.write` route; report any different route failure to
|
||||
adapter developers.
|
||||
|
||||
If it returns `extension_saved_state_prepare_protocol_unproven`, do not retry
|
||||
or supply technical coordinates. The adapter has not yet proven the exact
|
||||
saved-state preparation codec for this extension layout; report it for adapter
|
||||
development. Do not infer that the extension was not saved in Configurator.
|
||||
|
||||
## Hidden Storage Details
|
||||
|
||||
The form module container marker `///----` is adapter-owned storage syntax.
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
# Актуальный векторный поиск по коду 1С
|
||||
|
||||
Цель: семантический поиск по локальному SQL-индексу адаптера без потери
|
||||
актуальности кода. Вектор является только производным кешем: перед выдачей
|
||||
адаптер сверяет найденные фрагменты с текущим состоянием конфигурации и
|
||||
отбрасывает либо переиндексирует устаревшие записи.
|
||||
|
||||
## Выбранная модель
|
||||
|
||||
- `Qwen/Qwen3-Embedding-0.6B-GGUF`, квантование `Q8_0`;
|
||||
- OpenAI-compatible endpoint на `http://docker-gpu.cin.su:8082`;
|
||||
- `llama.cpp`, `--embedding --pooling last`;
|
||||
- CPU-only (`--n-gpu-layers 0`), чтобы не менять работающие GPU-сервисы;
|
||||
- endpoint возвращает 1024 измерения, клиент использует Matryoshka-срез до
|
||||
запрошенных 384 измерений и повторно нормализует его.
|
||||
|
||||
Образ `llama.cpp` зафиксирован digest, а модель — официальным repository/quant
|
||||
селектором. Это исключает незаметную смену runtime при повторном deploy.
|
||||
|
||||
Модель и образ публичные, endpoint работает в изолированном тестовом контуре
|
||||
без токена. Постоянный кеш модели хранится вне git в
|
||||
`Z:\LLM\models\cache\llama.cpp`.
|
||||
|
||||
## Развёртывание
|
||||
|
||||
Проверить итоговую конфигурацию:
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/deploy_embeddings.ps1 -ConfigOnly
|
||||
```
|
||||
|
||||
Запустить сервис с загрузкой образа:
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/deploy_embeddings.ps1 -Pull
|
||||
```
|
||||
|
||||
Первый запуск скачивает модель в постоянный кеш и поэтому может занять
|
||||
несколько минут. Скрипт ждёт `/health`, проверяет имя модели и делает реальный
|
||||
запрос к `/v1/embeddings`.
|
||||
|
||||
## Обновление векторов
|
||||
|
||||
Сначала адаптер должен содержать актуальные текстовые chunks. Затем worker
|
||||
забирает только отсутствующие либо изменившиеся фрагменты:
|
||||
|
||||
```powershell
|
||||
python scripts/embed_1c_code_vectors.py `
|
||||
--adapter-url http://docker.cin.su:8011/rpc `
|
||||
--base-id upo_test `
|
||||
--embedding-provider openai-compatible `
|
||||
--embedding-model qwen3-embedding-0.6b `
|
||||
--dimensions 384 `
|
||||
--embedding-base-url http://docker-gpu.cin.su:8082 `
|
||||
--limit 500 `
|
||||
--batch-size 8 `
|
||||
--chunk-kind routine `
|
||||
--max-text-chars 4000 `
|
||||
--json
|
||||
```
|
||||
|
||||
Worker по умолчанию индексирует `routine`: процедуры и функции дают наиболее
|
||||
точный контекст для программирования и заметно быстрее пересчитываются при
|
||||
частых изменениях. Для диагностического покрытия модульных фрагментов можно
|
||||
добавить второй `--chunk-kind module`; это более дорогой отдельный проход.
|
||||
`--max-text-chars` не обрезает код молча: длинные chunks пропускаются в этом
|
||||
проходе и остаются pending. Их нужно разбивать на окна отдельной задачей либо
|
||||
индексировать в период низкой нагрузки с большим лимитом.
|
||||
|
||||
Размерность входит в имя кеша (`openai-compatible:qwen3-embedding-0.6b@d384`),
|
||||
поэтому векторы разных размеров никогда не смешиваются.
|
||||
|
||||
## Поиск
|
||||
|
||||
```powershell
|
||||
python scripts/search_1c_code_vectors.py `
|
||||
"где рассчитывается сумма документа перед проведением" `
|
||||
--adapter-url http://docker.cin.su:8011/rpc `
|
||||
--base-id upo_test `
|
||||
--embedding-provider openai-compatible `
|
||||
--embedding-model qwen3-embedding-0.6b `
|
||||
--dimensions 384 `
|
||||
--embedding-base-url http://docker-gpu.cin.su:8082 `
|
||||
--limit 10 `
|
||||
--json
|
||||
```
|
||||
|
||||
Поиск вызывается с `strict=true` и `verify=true`. Сохранённые изменения
|
||||
Конфигуратора перекрывают активную конфигурацию, а удалённые/изменённые chunks
|
||||
не возвращаются по старому вектору.
|
||||
|
||||
Для активного кода расширений хеш `ConfigCAS` разрешается через текущий
|
||||
manifest расширения в descriptor объекта. Результат содержит обычные
|
||||
`kind/name`, а также `extension` и `extension_guid`; вызывающему коду не нужно
|
||||
работать с CAS-хешами как с именами объектов.
|
||||
|
||||
Перед первым глобальным поиском после обновления адаптера нужно постранично
|
||||
заполнить локальную карту владельцев:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "metadata.module_owner_cache.backfill",
|
||||
"payload": {
|
||||
"base_id": "upo_test",
|
||||
"limit": 50,
|
||||
"kind_index": 0,
|
||||
"offset": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Следующий вызов получает `kind_index` и `offset` из `next_cursor`. Повторять до
|
||||
`complete=true`. Операция читает актуальные метаданные, но пишет только в
|
||||
локальный SQLite адаптера. Найденные имена сразу добавляются в существующие
|
||||
строки лексического и векторного индексов; перестраивать embeddings не нужно.
|
||||
По умолчанию объекты без строк code index быстро пропускаются. `deep=true`
|
||||
нужен только для отдельного фонового заполнения владельцев неиндексированных
|
||||
модулей и не должен использоваться в интерактивном поиске.
|
||||
|
||||
Для `Qwen3-Embedding` клиент автоматически добавляет к векторизуемому запросу
|
||||
англоязычную инструкцию поиска по исходному коду 1С, как рекомендует карточка
|
||||
модели. В `query` адаптера остаётся исходный русский текст, поэтому лексическая
|
||||
часть гибридного поиска не загрязняется служебным префиксом.
|
||||
|
||||
## Отдельная векторная БД
|
||||
|
||||
На текущем этапе не требуется. Векторы хранятся рядом с индексом адаптера в
|
||||
SQLite и выбираются линейным сканированием. Это проще и гарантирует атомарную
|
||||
проверку актуальности. Отдельный ANN-движок имеет смысл только после замера
|
||||
десятков тысяч актуальных chunks и неприемлемой задержки; источником истины всё
|
||||
равно остаётся 1С/SQL, а ANN должен хранить `chunk_id` и `text_sha1` как
|
||||
проверяемую производную копию.
|
||||
@@ -6,7 +6,7 @@ form decoder reached zero missing items and zero mismatches.
|
||||
## Target
|
||||
|
||||
- Base: `upo_test`
|
||||
- Adapter: `http://docker-gpu.cin.su:8011`
|
||||
- Adapter: `http://docker.cin.su:8011`
|
||||
- Saved-state table: `ConfigCASSave`
|
||||
- Form payload file:
|
||||
`f96a0c45-3eff-11f1-8287-005056b0d483__25c39fbf-35a4-4b43-8e3f-cd1f91082c88.0`
|
||||
@@ -104,7 +104,7 @@ payload = {
|
||||
"timeout_seconds": 60,
|
||||
"max_items": 5000
|
||||
}
|
||||
req = urllib.request.Request("http://docker-gpu.cin.su:8011/rpc", data=json.dumps({"method":"metadata.write_learning.capture_after","payload":payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
req = urllib.request.Request("http://docker.cin.su:8011/rpc", data=json.dumps({"method":"metadata.write_learning.capture_after","payload":payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
print(urllib.request.urlopen(req, timeout=90).read().decode("utf-8"))
|
||||
'@ | python -
|
||||
```
|
||||
@@ -116,7 +116,7 @@ Then run:
|
||||
import json, urllib.request
|
||||
for method in ("metadata.write_learning.diff", "metadata.write_learning.infer_rule"):
|
||||
payload = {"learning_id": "form-command-binding-standard-customize-form"}
|
||||
req = urllib.request.Request("http://docker-gpu.cin.su:8011/rpc", data=json.dumps({"method": method, "payload": payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
req = urllib.request.Request("http://docker.cin.su:8011/rpc", data=json.dumps({"method": method, "payload": payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
print(urllib.request.urlopen(req, timeout=90).read().decode("utf-8"))
|
||||
'@ | python -
|
||||
```
|
||||
@@ -151,7 +151,7 @@ payload = {
|
||||
"timeout_seconds": 60,
|
||||
"max_items": 5000
|
||||
}
|
||||
req = urllib.request.Request("http://docker-gpu.cin.su:8011/rpc", data=json.dumps({"method":"metadata.write_learning.capture_after","payload":payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
req = urllib.request.Request("http://docker.cin.su:8011/rpc", data=json.dumps({"method":"metadata.write_learning.capture_after","payload":payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
print(urllib.request.urlopen(req, timeout=90).read().decode("utf-8"))
|
||||
'@ | python -
|
||||
```
|
||||
@@ -163,7 +163,7 @@ Then run:
|
||||
import json, urllib.request
|
||||
for method in ("metadata.write_learning.diff", "metadata.write_learning.infer_rule"):
|
||||
payload = {"learning_id": "form-command-binding-local-apply-command"}
|
||||
req = urllib.request.Request("http://docker-gpu.cin.su:8011/rpc", data=json.dumps({"method": method, "payload": payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
req = urllib.request.Request("http://docker.cin.su:8011/rpc", data=json.dumps({"method": method, "payload": payload}, ensure_ascii=False).encode("utf-8"), headers={"Content-Type":"application/json"})
|
||||
print(urllib.request.urlopen(req, timeout=90).read().decode("utf-8"))
|
||||
'@ | python -
|
||||
```
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# 1C Form Discovery And Editing
|
||||
|
||||
The semantic object/form boundary for agent responses is defined in
|
||||
[`1c-metadata-structure.md`](1c-metadata-structure.md). In particular, an
|
||||
owner returns references to forms; form parameters, attributes, commands,
|
||||
elements, and form-module facts are read through the form's own public
|
||||
selector. Do not duplicate a decoded form as an invented subtree of its owner.
|
||||
|
||||
This runbook adapts the MOXCEL discovery loop to managed forms. The goal is a
|
||||
full SQL-side form decoder and safe saved-state editing through the test
|
||||
extension, with XML exports used only as evidence fixtures.
|
||||
@@ -7,7 +13,7 @@ extension, with XML exports used only as evidence fixtures.
|
||||
## Current Baseline
|
||||
|
||||
- Default base: `upo_test`.
|
||||
- Default adapter endpoint: `http://docker-gpu.cin.su:8011`.
|
||||
- Default adapter endpoint: `http://docker.cin.su:8011`.
|
||||
- Primary test extension/object fixture:
|
||||
`фс_ДоработкиОбщее` /
|
||||
`DataProcessor.фс_НастройкаУсловногоОформления`.
|
||||
@@ -142,7 +148,7 @@ Decode a concrete saved-state form payload:
|
||||
|
||||
```powershell
|
||||
python scripts/smoke_1c_write_matrix.py `
|
||||
--base-url http://docker-gpu.cin.su:8011 `
|
||||
--base-url http://docker.cin.su:8011 `
|
||||
--base-id upo_test `
|
||||
--table ConfigCASSave `
|
||||
--file-name <form-file-name> `
|
||||
@@ -177,7 +183,7 @@ Build a decoder coverage and gap profile for an object form:
|
||||
|
||||
```powershell
|
||||
python scripts/profile_1c_forms.py `
|
||||
--adapter-url http://docker-gpu.cin.su:8011 `
|
||||
--adapter-url http://docker.cin.su:8011 `
|
||||
--base-id upo_test `
|
||||
--kind Catalog `
|
||||
--name ЗадачиАссистентаУправления `
|
||||
@@ -223,7 +229,7 @@ Run the existing source-aware route smoke:
|
||||
|
||||
```powershell
|
||||
python scripts/smoke_1c_saved_state_write_routes.py `
|
||||
--base-url http://docker-gpu.cin.su:8011 `
|
||||
--base-url http://docker.cin.su:8011 `
|
||||
--base-id upo_test `
|
||||
--table ConfigCASSave `
|
||||
--file-name <form-file-name> `
|
||||
@@ -234,7 +240,7 @@ Then run the matrix smoke:
|
||||
|
||||
```powershell
|
||||
python scripts/smoke_1c_write_matrix.py `
|
||||
--base-url http://docker-gpu.cin.su:8011 `
|
||||
--base-url http://docker.cin.su:8011 `
|
||||
--base-id upo_test `
|
||||
--table ConfigCASSave `
|
||||
--file-name <form-file-name> `
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
# Структура метаданных конфигурации 1С
|
||||
|
||||
Этот документ задаёт модель, которой должны следовать агенты и публичные
|
||||
ответы SQL-адаптера. Он описывает семантические метаданные, а не физические
|
||||
имена SQL-записей, пути контейнеров или номера потоков.
|
||||
|
||||
## Главное правило
|
||||
|
||||
У конфигурации нет единого шаблона дочерних узлов для всех классов объектов.
|
||||
Набор узлов определяется классом объекта и подтверждается двумя источниками:
|
||||
|
||||
1. документацией платформы 1С для соответствующего класса;
|
||||
2. живым чтением выбранного объекта через адаптер.
|
||||
|
||||
Не добавляйте в ответ ни табличную часть, ни форму, ни модуль только потому,
|
||||
что они есть у другого объекта. Если живой декодер не подтвердил конкретный
|
||||
дочерний объект, верните явный неполный результат, а не пустой вымышленный
|
||||
узел.
|
||||
|
||||
## Связь объекта и формы
|
||||
|
||||
Форма — самостоятельный объект метаданных, на который владелец ссылается в
|
||||
своей коллекции `Формы`. В кратком описании владельца возвращается только
|
||||
ссылка/идентичность формы: имя, GUID и доступный публичный селектор. Полное
|
||||
описание формы читается отдельным запросом.
|
||||
|
||||
```text
|
||||
Отчет
|
||||
└─ Формы
|
||||
└─ <ссылка на Форму>
|
||||
|
||||
Форма
|
||||
├─ Реквизиты
|
||||
├─ Параметры
|
||||
├─ Команды
|
||||
├─ Модуль формы
|
||||
└─ Элементы
|
||||
```
|
||||
|
||||
Параметры формы не являются её реквизитами. Это декларация параметризации при
|
||||
открытии формы. Команда формы не имеет отдельного модуля: её имя обработчика
|
||||
является свойством команды и разрешается в модуле формы. Аналогично имя
|
||||
обработчика события элемента — свойство элемента, а не дочерний узел дерева.
|
||||
|
||||
Подчинённая команда владельца (`Отчет.Команды.<Имя>`) отличается от команды
|
||||
формы. Если документация и живая база подтверждают модуль команды, его надо
|
||||
сообщать как свойство этой команды, не выдавая за форму или за модуль формы.
|
||||
|
||||
## Отчёт
|
||||
|
||||
Официальная методическая документация 1С подтверждает, что отчёт может иметь
|
||||
реквизиты и табличные части. Табличная часть, в свою очередь, имеет реквизиты.
|
||||
Для отчёта допустима следующая *классовая* схема; конкретные экземпляры
|
||||
показываются только после живого чтения:
|
||||
|
||||
```text
|
||||
Отчет
|
||||
├─ Реквизиты
|
||||
├─ Табличные части
|
||||
│ └─ Реквизиты табличной части
|
||||
├─ Формы → отдельные описания Форм
|
||||
├─ Команды
|
||||
├─ Макеты
|
||||
├─ Модуль объекта
|
||||
└─ Модуль менеджера
|
||||
```
|
||||
|
||||
СКД — это тип/содержимое макета, а не обязательная отдельная ветвь любого
|
||||
отчёта. Не создавайте узел СКД, если в живом объекте не подтверждён
|
||||
соответствующий макет.
|
||||
|
||||
## Публичные маршруты адаптера
|
||||
|
||||
Для объекта `Отчет.<Имя>` адаптер использует следующие раздельные операции:
|
||||
|
||||
1. `metadata.object.forms` — читает подтверждённые ссылки отчёта на формы;
|
||||
возвращаемые строки содержат идентичность формы, а не её элементы,
|
||||
реквизиты, параметры или текст модуля.
|
||||
2. `metadata.object.form.details` — по владельцу и имени формы получает
|
||||
развёрнутое описание формы.
|
||||
3. `metadata.form.decode` — декодирует одну конкретную форму и возвращает её
|
||||
параметры, реквизиты, элементы, команды, события и сводку встроенного
|
||||
модуля.
|
||||
|
||||
Передавайте `configuration_view=effective_working` либо не передавайте view:
|
||||
MCP установит этот режим сам. Он означает логическую рабочую конфигурацию с
|
||||
учётом базовой конфигурации, сохранённых изменений и расширений; он не
|
||||
разрешает агенту выбирать `ConfigSave` или `ConfigCASSave`.
|
||||
|
||||
Таким образом, ссылка `Отчет → Форма` не должна заменяться копией описания
|
||||
формы внутри объекта отчёта. Агрегированный `metadata.object.full` удобен для
|
||||
обзора, но для работы с формой агент обязан сохранять её отдельный публичный
|
||||
селектор и при необходимости вызвать один из двух form-методов выше.
|
||||
|
||||
Текущая реализация подтверждает маршрут для `Report`: в
|
||||
`RELATED_SECTION_RULES` форма имеет категорию `Form`, а
|
||||
`metadata.object.forms` возвращает только `guid`, `name`, `synonyms` и счётчики
|
||||
частей. Детали добавляет только `metadata.object.form.details` через отдельный
|
||||
вызов `metadata.form.decode`.
|
||||
|
||||
## Источники
|
||||
|
||||
- [Иерархия объектов конфигурации и подчинённые объекты — 1С:EDT](https://its.1c.ru/db/content/edtdoc/src/topics/i030.html)
|
||||
- [Отчёты, реквизиты и табличные части — методическая поддержка 1С](https://its.1c.ru/db/content/metod8dev/src/platform81/startersdev/i8102579.htm)
|
||||
- [Формы: реквизиты, параметры, команды и модуль — руководство разработчика 1С](https://its.1c.ru/db/v8310doc/bookmark/dev/TI000000391)
|
||||
- [Явное объявление параметров формы — стандарт 1С](https://its.1c.ru/db/content/v8std/src/1%C2%A0200/700/i8100741.htm)
|
||||
|
||||
## Проверка перед изменением
|
||||
|
||||
Перед тем как менять форму или модуль, агент обязан выполнить публичное
|
||||
чтение владельца, затем получить форму её собственным селектором. Отсутствие
|
||||
SQL-подключения, нераспознанный контейнер или неоднозначная связь — это
|
||||
`unresolved`/`protocol_incomplete`, а не разрешение достроить структуру по
|
||||
документации.
|
||||
|
||||
`metadata.form.decode` с `view=structure` возвращает компактные доказанные
|
||||
факты по статическим элементам. Пока SQL-кодек логического родителя не
|
||||
доказан, поля `parent` и `children` имеют статус `unresolved`; нельзя строить
|
||||
их из технической глубины или пути сериализованного контейнера.
|
||||
|
||||
Запросы `runtime.form.elements.inspect` и `runtime.form.inspect` возвращают
|
||||
`runtime_inspection_unsupported`. SQL-адаптер не открывает формы, не запускает
|
||||
обработчики и не выдаёт созданные СКД во время работы элементы за статические
|
||||
метаданные.
|
||||
@@ -240,7 +240,7 @@ changes:
|
||||
|
||||
```powershell
|
||||
python scripts\profile_1c_tabular_templates.py `
|
||||
--adapter-url http://docker-gpu.cin.su:8011 `
|
||||
--adapter-url http://docker.cin.su:8011 `
|
||||
--base-id upo_test `
|
||||
--inventory-json reports\1c-template-baselines\upo_test_configuration_tabular_templates.json `
|
||||
--output-json reports\1c-template-baselines\upo_test_tabular_template_profiles.json `
|
||||
|
||||
@@ -116,8 +116,11 @@ SQL удобен как быстрый источник данных, но не
|
||||
canonical path до процедуры, областью является эта процедура/функция; иначе
|
||||
весь текущий saved-модуль.
|
||||
|
||||
По умолчанию `code.write` делает `mode=apply`, но это apply в saved-state
|
||||
слой (`ConfigSave`/`ConfigCASSave`), а не применение конфигурации в runtime.
|
||||
По умолчанию `code.write` делает безопасный `mode=plan` и не пишет в SQL.
|
||||
Только явно переданный `mode=apply`, `apply_and_verify` или
|
||||
`apply_and_rollback` может записать saved-state слой
|
||||
(`ConfigSave`/`ConfigCASSave`); это всё равно не применение конфигурации в
|
||||
runtime.
|
||||
Адаптер сам выставляет save-first gates и сам выбирает физический маршрут.
|
||||
Физические детали возвращаются только при `include_storage=true` для
|
||||
диагностики. Ответ `code.write` всегда содержит `write_mode`: target
|
||||
@@ -134,6 +137,62 @@ source `saved_state`, activation_state `not_activated`.
|
||||
добирается отдельным проходом, а `counts.saved_matches` и
|
||||
`counts.active_matches` показывают покрытие по слоям.
|
||||
|
||||
### Repository lock and first extension edit
|
||||
|
||||
Для изменения объекта в расширении, подключенном к хранилищу, агент сначала
|
||||
использует только публичные вызовы:
|
||||
|
||||
```text
|
||||
code.search → repository.lock.plan → repository.lock.request
|
||||
→ человек захватывает объект в Конфигураторе → repository.lock.confirm
|
||||
→ code.write → code.search (readback)
|
||||
```
|
||||
|
||||
Если у extension-модуля ещё нет saved-state строки, `code.write(mode=plan)`
|
||||
возвращает `needs_prepare` и
|
||||
`diagnostics.next_action=confirm_repository_lock_then_apply`. Это нормальный
|
||||
первый-edit маршрут: после подтверждённого lock тот же публичный `code.write`
|
||||
в apply-режиме сам подготовит saved-state. Агент не передаёт `module_ref`,
|
||||
`stream_index`, таблицу или имя технического файла.
|
||||
|
||||
Исключение: `extension_saved_state_prepare_protocol_unproven` означает, что
|
||||
автоматическая подготовка запрещена. Это не доказательство того, что
|
||||
расширение не сохраняли: адаптер ещё не доказал точный prepare-кодек для
|
||||
наблюдаемой extension-layout. Агент не создаёт контейнер через SQL и передаёт
|
||||
случай разработчикам адаптера без технических координат.
|
||||
|
||||
Acceptance extension write считается пройденным только при наличии в
|
||||
`upo_test` отдельного extension-owned `Report` с object module и успешном
|
||||
публичном `code.write(..., apply_and_rollback)` без storage-координат. Успех
|
||||
base-модуля в `ConfigSave` не доказывает ветку `ConfigCAS → ConfigCASSave`.
|
||||
|
||||
`upo` не используется для автоматических проверочных записей. Контролируемые
|
||||
`apply_and_rollback` проверки разрешены только в `upo_test`.
|
||||
|
||||
Для регрессии первого extension-edit используйте публичный smoke (без SQL
|
||||
таблиц, key, module_ref или GUID в запросе):
|
||||
|
||||
```powershell
|
||||
python scripts\smoke_1c_extension_saved_state_prepare.py --apply
|
||||
```
|
||||
|
||||
Он проверяет `plan → prepare/readback → rollback → immediate plan` на
|
||||
`upo_test / фс_ДоработкиОбщее / Catalog.Номенклатура`. После rollback не
|
||||
должно остаться saved-state строк, а повторный план должен быть `plan_ready`.
|
||||
|
||||
### Safe adapter deployment
|
||||
|
||||
Перед Docker-обновлением скрипт развёртывания запрашивает `/health` и ждёт
|
||||
`runtime.state=ready` и `runtime.active_rpc_count=0`. При остановке REST
|
||||
переходит в `draining`; уже начатые запросы продолжают выполняться до пяти
|
||||
минут. Не используйте `-SkipDrainCheck`, кроме аварийного случая, когда
|
||||
ответственный подтвердил отсутствие активной записи.
|
||||
|
||||
После обновления проверяются REST `/health?base_id=upo_test` и MCP `/health`.
|
||||
JSONL-аудит REST хранится в `/data/adapter-audit.jsonl`, MCP — в
|
||||
`/data/mcp-audit.jsonl`; оба периодически сворачиваются в
|
||||
`/data/adapter-audit-reports/latest.json` на соответствующем хосте.
|
||||
|
||||
Если фрагмент повторяется, агент должен передать более узкий контекст
|
||||
(`routine_name`) или заменить процедуру целиком. Адаптер в такой ситуации
|
||||
возвращает `ambiguous_fragment`, `scope` и `counts.occurrences`, и не
|
||||
|
||||
@@ -52,7 +52,7 @@ MCP server configuration.
|
||||
The MCP proxy does not hard-code the 1C adapter address. Pass it with:
|
||||
|
||||
```text
|
||||
ONEC_ADAPTER_URL=http://docker-gpu.cin.su:8011
|
||||
ONEC_ADAPTER_URL=http://docker.cin.su:8011
|
||||
```
|
||||
|
||||
Optional adapter bearer token:
|
||||
@@ -75,6 +75,37 @@ core/deploy/docker/adapter-1c-mcp/.env.example
|
||||
|
||||
For real deployment, create a non-committed `.env` next to the compose file and set the actual adapter URL/token there.
|
||||
|
||||
## REST adapter on docker.cin.su
|
||||
|
||||
The REST SQL adapter is deployed from:
|
||||
|
||||
```text
|
||||
core/deploy/docker/adapter-1c/compose.yaml
|
||||
```
|
||||
|
||||
Its published port is set in a non-committed REST environment file:
|
||||
|
||||
```text
|
||||
ADAPTER_1C_HOST_PORT=8011
|
||||
```
|
||||
|
||||
SQL-base administration is available in the REST adapter at:
|
||||
|
||||
```text
|
||||
http://docker.cin.su:<ADAPTER_1C_HOST_PORT>/admin
|
||||
```
|
||||
|
||||
With the default port this is `http://docker.cin.su:8011/admin`. The page is
|
||||
used to add or update SQL connections for named bases. Do not put connection
|
||||
passwords in Git or documentation. If the port changes, use the new port both
|
||||
for this page and in `ONEC_ADAPTER_URL`.
|
||||
|
||||
If that port is occupied, select a free port there and set the MCP URL to the
|
||||
same value, for example `ONEC_ADAPTER_URL=http://docker.cin.su:18011`.
|
||||
Keep `ONEC_SQL_BASES_JSON` (or the mounted `/data/onec-sql-bases.json`) only
|
||||
in that external runtime configuration; never place SQL passwords in this
|
||||
repository.
|
||||
|
||||
## Deploy
|
||||
|
||||
Deploy both REST adapter and MCP proxy, then run live verification when a test
|
||||
@@ -83,6 +114,8 @@ base is available:
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass `
|
||||
-File scripts\deploy_1c_adapter_stack.ps1 `
|
||||
-RestEnvFile <path-to-non-committed-rest-env> `
|
||||
-McpEnvFile <path-to-non-committed-mcp-env> `
|
||||
-BaseId <base-id-from-project-context>
|
||||
```
|
||||
|
||||
@@ -692,7 +725,7 @@ Saved-state BSL write smoke:
|
||||
|
||||
```powershell
|
||||
python scripts\smoke_1c_code_write_saved_state.py `
|
||||
--adapter-url http://docker-gpu.cin.su:8011 `
|
||||
--adapter-url http://docker.cin.su:8011 `
|
||||
--base-id <base-id-from-project-context> `
|
||||
--extension <extension-name> `
|
||||
--object-type CommonForm `
|
||||
@@ -712,7 +745,7 @@ Agent working-view report:
|
||||
|
||||
```powershell
|
||||
python scripts\report_1c_agent_working_view.py `
|
||||
--adapter-url http://docker-gpu.cin.su:8011 `
|
||||
--adapter-url http://docker.cin.su:8011 `
|
||||
--base-id <base-id-from-project-context> `
|
||||
--extension <extension-name> `
|
||||
--object-type CommonForm `
|
||||
@@ -738,7 +771,7 @@ Optional live selector-chain smoke against a real adapter/base:
|
||||
python scripts\smoke_1c_mcp_selector_chain.py `
|
||||
--live `
|
||||
--transport rest `
|
||||
--adapter-url http://docker-gpu.cin.su:8011 `
|
||||
--adapter-url http://docker.cin.su:8011 `
|
||||
--base-id <base-id-from-project-context> `
|
||||
--json `
|
||||
--no-report
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
# Adapter Observer
|
||||
|
||||
`adapter-observer` is an independent, read-only web service for adapter telemetry.
|
||||
It must never connect to 1C SQL, call write methods, modify `/data`, or be a
|
||||
runtime dependency of `adapter-1c-rest` or `adapter-1c-mcp`.
|
||||
|
||||
## Inputs and guarantees
|
||||
|
||||
- It mounts REST and MCP Docker volumes at `/audit:ro` and `/mcp-audit:ro` and
|
||||
reads only rotated `adapter-audit.jsonl*` / `mcp-audit.jsonl*` files.
|
||||
- The source events contain public selector summaries, status/error, request
|
||||
correlation id, timings, and safe result summaries. They intentionally omit
|
||||
BSL text, raw SQL payloads, storage keys, and credentials.
|
||||
- A missing or malformed log is an Observer condition, never an adapter error.
|
||||
- The UI distinguishes `exception` from expected safe outcomes such as
|
||||
`blocked`, `unsupported`, and `invalid_argument`.
|
||||
|
||||
## Tabs
|
||||
|
||||
- **Журнал запросов**: filterable individual REST events and their safe details.
|
||||
- **Аналитика**: p50/p95, slow method ranking, grouped non-OK fingerprints and
|
||||
evidence-based next actions.
|
||||
- **Покрытие**: contract for adding a future `metadata.adapter.audit` snapshot.
|
||||
|
||||
## Retry boundary
|
||||
|
||||
Audit JSONL deliberately has no complete request payload. Do not reconstruct
|
||||
or guess it from selector summaries. A future replay button may repeat only a
|
||||
request captured by Observer itself with an explicit read-only allowlist. It
|
||||
must never replay a write request from logs.
|
||||
|
||||
## Deployment
|
||||
|
||||
On `docker.cin.su`, inspect the existing REST container and volume first:
|
||||
|
||||
```text
|
||||
docker inspect adapter-1c-rest
|
||||
docker volume ls
|
||||
```
|
||||
|
||||
Deploy only the Observer stack from `core/deploy/docker/adapter-observer`.
|
||||
It uses external volume `adapter-1c_adapter-1c-data` read-only and port 8031
|
||||
by default. Do not run `down` against the adapter compose project.
|
||||
|
||||
```text
|
||||
docker compose --env-file .env -f compose.yaml up -d --build
|
||||
curl http://localhost:8031/health
|
||||
```
|
||||
|
||||
## Development contract
|
||||
|
||||
When adapter telemetry changes, preserve backwards parsing: unknown fields are
|
||||
shown in event details; known metrics remain optional. Before adding a special
|
||||
visualization, record its input schema and add fixture JSONL tests. The general
|
||||
journal must continue working for unknown adapter methods.
|
||||
|
||||
## Key decisions for future agents
|
||||
|
||||
1. Keep Observer a separate Compose project, port, image and failure domain.
|
||||
Never add it as a dependency to REST/MCP and never restart those containers
|
||||
while deploying it.
|
||||
2. The volumes are externally named `adapter-1c_adapter-1c-data` and
|
||||
`adapter-1c-mcp_adapter-1c-mcp-data` on `docker.cin.su`; Observer mounts
|
||||
them only as `/audit:ro` and `/mcp-audit:ro`.
|
||||
3. Audit events are evidence, not replay payloads. The historical journal can
|
||||
link to an object by a public selector but cannot reconstruct omitted fields.
|
||||
4. Treat job lifecycle statuses `accepted`, `running`, `done` and `cancelled`
|
||||
as operational state, not failures. Expected rejections are shown separately
|
||||
from adapter exceptions.
|
||||
5. Retain both `duration_ms` (Observer-facing REST wall time) and optional
|
||||
result timings. Do not manufacture nested timings if the adapter did not
|
||||
return them; the first precise per-span waterfall requires an Observer-owned
|
||||
read-only proxy/session trace.
|
||||
|
||||
## Delivery plan
|
||||
|
||||
### Delivered MVP
|
||||
|
||||
- Rotated REST JSONL reader, safe event details and filters.
|
||||
- p50/p95/max latency by method, error fingerprint grouping and guidance.
|
||||
- Read-only Docker deployment and health endpoint.
|
||||
- MCP-to-REST request correlation by `request_id`; absent REST pair is shown as
|
||||
a transport boundary, not a decoder failure.
|
||||
- A bounded read-only coverage refresh (`help.methods` and
|
||||
`metadata.adapter.audit`) stored in Observer's own state volume, with the
|
||||
latest 50 snapshots per base and a schema/method/count delta.
|
||||
- A best-effort background coverage snapshot every 900 seconds for `upo_test`.
|
||||
It has a 300-second minimum interval and must never affect UI availability.
|
||||
|
||||
### Next safe increments
|
||||
|
||||
1. Add an Observer-owned, read-only allowlist proxy. It can save complete
|
||||
*sanitized* read request payloads for user-initiated replay and form a true
|
||||
parent/child waterfall; no historical write replay.
|
||||
2. Periodically invoke `metadata.adapter.audit` through that proxy and persist
|
||||
versioned coverage snapshots in an Observer-owned SQLite database.
|
||||
3. Add regression screens: compare coverage/schema/method catalog before and
|
||||
after an adapter release, with an explicit `not comparable` state.
|
||||
4. Add retention, export and role controls before exposing the journal outside
|
||||
the internal network.
|
||||
|
||||
### Explicit non-goals until separately approved
|
||||
|
||||
- No replay of historical write, activation, repository or password operations.
|
||||
- No raw request/response capture solely to make replay convenient.
|
||||
- No automatic remediation, SQL optimization, Configurator launch or mutation.
|
||||
- No claim that a slow request is an adapter defect without repeated evidence.
|
||||
|
||||
### Items commonly forgotten in observability work
|
||||
|
||||
- **Clock semantics:** preserve source UTC timestamp and Observer receive time;
|
||||
never compare durations across hosts as if clocks were synchronized.
|
||||
- **Asynchrony:** display job poll calls separately from end-to-end job time;
|
||||
polling volume must not dominate failure charts.
|
||||
- **Cardinality:** bound selector/error fingerprints so arbitrary object names
|
||||
cannot create an unbounded metrics index.
|
||||
- **Release correlation:** record Observer version and adapter health/method
|
||||
catalog snapshot beside each periodic coverage run.
|
||||
- **Availability:** health, disk/read errors and log rotation failures of
|
||||
Observer must appear in its own diagnostics, not as adapter failures.
|
||||
- **Retention and access:** define data lifetime, exported fields and viewer
|
||||
permissions before making port 8031 internet-facing or adding Caddy routes.
|
||||
|
||||
## Review checklist
|
||||
|
||||
- Does a proposed feature work with omitted payload fields rather than guessing?
|
||||
- Does it classify expected rejection separately from exception/transport loss?
|
||||
- Does it preserve `request_id` and public selector provenance?
|
||||
- Is every newly persisted field redacted and bounded by retention?
|
||||
- Can the Observer be stopped without affecting adapter requests?
|
||||
@@ -0,0 +1,50 @@
|
||||
# Прикладной эталон дополнительных реквизитов для разработки
|
||||
|
||||
## Цель
|
||||
|
||||
Опубликовать в изолированной базе `upo_test` read-only endpoint, который
|
||||
читает ПВХ средствами платформы 1С. Это эталон для разработки и регрессионного
|
||||
сравнения SQL-адаптера; адаптер к endpoint не подключается.
|
||||
|
||||
## Изолированное расширение
|
||||
|
||||
1. В Конфигураторе создайте новое расширение `AdapterAdditionalAttributesBridge`.
|
||||
2. Добавьте серверный общий модуль `ДополнительныеРеквизитыReadOnly` и вставьте
|
||||
содержимое `plugins/1c/bridge/additional_attributes_readonly.bsl`.
|
||||
3. Добавьте HTTP-сервис `AdapterRuntimeBridge` с URL `/runtime-bridge`.
|
||||
4. Добавьте URL-шаблон `rpc`, метод `POST`, и обработчик
|
||||
`ДополнительныеРеквизитыHTTP.ОбработатьRPC`.
|
||||
5. Добавьте серверный модуль `ДополнительныеРеквизитыHTTP` и вставьте
|
||||
`plugins/1c/bridge/additional_attributes_http_handler.bsl`.
|
||||
6. Ограничьте публикацию тестовой сетью и отдельным техническим пользователем
|
||||
только с правами чтения ПВХ и справочника `СтруктурныеЕдиницы` вместе с его
|
||||
табличной частью `ДополнительныеРеквизиты`.
|
||||
7. Обновите конфигурацию базы из расширения и опубликуйте HTTP-сервис.
|
||||
|
||||
Не меняйте существующие `Chatbot`, биллинг, телефонию или сервисы обмена.
|
||||
|
||||
## Проверка endpoint
|
||||
|
||||
```json
|
||||
{"method":"additional_attributes.find","payload":{"base_id":"upo_test","query":"Ответственное направление","include_deleted":false,"limit":10}}
|
||||
```
|
||||
|
||||
Ожидается `status=found` и как минимум `ref`, `description`,
|
||||
`identifier_for_formula`, `property_set`, `value_type`.
|
||||
|
||||
Затем:
|
||||
|
||||
```json
|
||||
{"method":"additional_attributes.storage.resolve","payload":{"base_id":"upo_test","property_ref":"<UUID свойства>","owner_ref":"Catalog.СтруктурныеЕдиницы"}}
|
||||
```
|
||||
|
||||
Ожидается `status=confirmed` и источник
|
||||
`Справочник.СтруктурныеЕдиницы.ДополнительныеРеквизиты`.
|
||||
|
||||
## Использование результата
|
||||
|
||||
Сравните ответ endpoint с `data.list`, `data.schema`,
|
||||
`additional_attributes.find` и `additional_attributes.storage.resolve` SQL
|
||||
адаптера. В код адаптера переносятся только подтверждённые общие правила
|
||||
разбора метаданных и SQL-маршрута; адрес endpoint, учётные данные и вызовы
|
||||
платформы в адаптер не добавляются.
|
||||
Reference in New Issue
Block a user