# 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?
