# Configured staging adapter — v0.3.2

Trace can now exercise an application across an HTTP boundary. The application must implement the contract below, usually through a small adapter around its existing staging ingestion, search, deletion and inventory paths. No paid services or optional Python packages are needed by the runner.

This is the first integration mechanism, not a claim of tested support for arbitrary products. In addition to synthetic demos, a local service backed by unmodified Mem0 2.2.1 has now been assessed. See [MEM0_STAGING.md](MEM0_STAGING.md) for its real upstream paths, local embedding configuration, retained-history evidence and coverage limits. No customer or hosted deployment has been assessed.

## Try it locally

In the extracted project directory, start the independent demo in one terminal:

```text
python scripts/demo_staging_app.py
```

In another terminal:

```text
python scripts/launch.py external-plan --config examples/staging-adapter.json
python scripts/launch.py external-run --config examples/staging-adapter.json --output reports/staging-fixed.json
```

The plan command makes no network requests. The run command creates synthetic records and invokes deletion only for IDs generated during that run. The healthy demo should return PASS and complete cleanup. Stop the demo, restart it with `--defect stale-cache`, and repeat with a different report filename: expect FAIL with surviving cached data and `cleanup_complete: false`. Restarting the demo clears its in-memory data.

Exit codes: 0 PASS, 1 FAIL, 2 INCONCLUSIVE/configuration error. Runs always checkpoint evidence under the selected data directory's `external/` folder, and append completed reports to its SQLite ledger. `--output` writes an additional final JSON export. Every issued ID is checkpointed before its ingest request. A running/incomplete checkpoint is not a final PASS. The checkpoint path is printed to stderr. External history stays separate from the reference dashboard and ranking.

## Register, save, replay and compare

Use the same `--data-dir` across CLI and MCP to share the registered targets and evidence:

```text
python scripts/launch.py external-register --target my-staging --config examples/staging-adapter.json
python scripts/launch.py external-targets
python scripts/launch.py external-plan --target my-staging
python scripts/launch.py external-run --target my-staging --output reports/before.json
python scripts/launch.py external-save BEFORE_RUN_ID
python scripts/launch.py external-replay REGRESSION_ID --output reports/after.json
python scripts/launch.py external-compare BEFORE_RUN_ID AFTER_RUN_ID
python scripts/launch.py external-show AFTER_RUN_ID
```

Only a completed FAIL can become a saved regression. A saved definition fixes the registered target, protocol revision, timing, document count and assertion contract. Replay rejects definition drift. Comparison rejects different target IDs or contracts and reports both cleanup outcomes. It runs fresh synthetic data through the same lifecycle; it does not prove old leaked records have been cleaned up or that a source patch was applied. Inspect and clean historical residuals separately.

Target registration is immutable: reuse of a target ID with a different config is rejected. Register a different ID for a different endpoint or configuration; those runs cannot be compared as if they used the same target. `external-run --config …` remains supported and registers a deterministic config-derived target ID. Plans do not contact the application. Reports and regression definitions are append-only with payload digests; this detects accidental corruption, not a malicious administrator replacing the local ledger.

## External workflow through MCP

Generate the local connection using `python scripts/launch.py mcp-config`. The server now exposes 15 tools: the original eight reference tools plus `trace.external_targets`, `trace.external_plan`, `trace.external_start`, `trace.external_get`, `trace.external_save`, `trace.external_replay`, and `trace.external_compare`.

Register staging targets through the local CLI first. MCP accepts registered IDs only, with no URL, config-file or registration arguments. Start/replay perform network requests and create/delete synthetic documents; their MCP annotations declare those effects. They return run IDs immediately. Poll `external_get` until `state` is `completed`, `failed`, or `interrupted`; dispatch is not completion. After a process restart, a checkpoint without a completed ledger record is returned as `interrupted`/INCONCLUSIVE, never promoted to a trusted PASS. Inspect issued IDs and cleanup before rerunning. There is one active external job per server process; separate processes can still run concurrently with distinct namespaces.

Example assistant instruction: “List registered external targets and inspect the plan for my-staging. Run its synthetic deletion contract, poll for the result, and show findings and cleanup status. If it fails, save the regression. After I fix the staging app, replay that saved regression, poll, and compare the runs without changing the assertions.”

## Connect a real staging application

Copy the example config and replace `base_url` with the adapter's URL. Set `environment` to `staging` and `allow_synthetic_writes` to true only for a test environment you control. These are operator declarations, not automatic proof that the server is staging. The API contract is fixed; unknown config keys are rejected.

For remote HTTPS endpoints, an optional `token_env` names an existing environment variable holding a scoped bearer token. Its value stays in memory, never in a config or evidence report. Local HTTP is accepted only for loopback hostnames. TLS validation stays enabled. Redirects are rejected and environment proxies are bypassed. Credentials embedded in URLs, query strings and URL fragments are rejected.

The runner supplies independent random `trace-test-…` and `trace-control-…` namespaces. The staging adapter must isolate these namespaces from real data and route calls through the application's real lifecycle paths. It must preserve Trace's supplied IDs and delete only the supplied IDs in the supplied namespace. Treat unknown IDs as an idempotent deletion. Do not implement namespace-wide or tenant-wide deletion as a shortcut.

Every operation is HTTP POST to `base_url + /operation`. Successful responses must be HTTP 200 with a JSON object. Responses are limited to 1 MiB; requests use the configured per-operation timeout. No request is automatically retried, since a failed response may follow a successful write. Avoid real user text in responses. Trace records only inventory counts and whether its synthetic marker appeared, not returned answer text or raw server error bodies.

| Operation | Request | Response |
| --- | --- | --- |
| `/ingest` | `{"namespace":"…","id":"trace-doc-…","text":"Synthetic … trace_canary_…"}` | `{"id":"same supplied ID"}` |
| `/query` | `{"namespace":"…","query":"trace_canary_…"}` | `{"text":"application retrieval or cached answer"}` |
| `/inventory` | `{"namespace":"…"}` | `{"complete":true,"source_records":3,"vector_records":3,"cache_records":1}` |
| `/delete` | `{"namespace":"…","ids":["trace-doc-…"]}` | `{"acknowledged":true}` |

Counts must be nonnegative JSON integers, never booleans, nulls or omitted fields. `complete: true` means full pagination and all declared stores were actually enumerated for that namespace. Return `complete: false` when inspection is unavailable. A retrieval endpoint alone cannot establish complete store erasure. Trace validates response shape but cannot establish whether an adapter is honest about its inventory coverage.

## Timing and verdicts

Ingestion must be fully visible on return for this protocol version. Before deletion, the configured document count must exist in source and vector inventories, the marker must be retrievable, and a separate synthetic control must be present and retrievable. A query warms the application cache through its usual path.

The observation window begins after the deletion acknowledgment. Trace samples immediately and continues until a sample begins at or after `deletion_window_seconds`. Each sample checks inventories, the original query and the unrelated control. A clean early sample cannot end the run: an ingestion worker might restore records later. Data remaining at the final sample produces FAIL. Missing observations, failed baseline, request errors or lost controls produce INCONCLUSIVE. Transient residuals that disappear within the declared window are recorded and do not by themselves fail the final-window contract.

Polling is sequential and request latency can lengthen the window. Timestamps describe sampled observations, not an exact continuous deadline guarantee. Reappearance after the final observation or between samples can be missed. Controlled worker barriers and pagination stress are future external scenarios; the existing reference fixtures still demonstrate them locally.

Cleanup always attempts deletion for every issued ID, including requests whose acknowledgment was lost. It polls each cleanup inventory through the same configured deletion window, allowing declared propagation delays. It records acknowledgments and remaining inventories for both namespaces. Cleanup failure is explicit; an otherwise PASS run becomes INCONCLUSIVE. A confirmed deletion FAIL stays FAIL even when its residuals cannot be cleaned up. Review the report's cleanup section and remove residual synthetic data through the staging application's normal tools. Interrupts/process termination can prevent cleanup; the checkpoint's `issued` map lists namespaces and IDs for investigation. Do not run two commands concurrently with the same output path.

Responses with duplicate JSON keys, non-finite constants, truncated HTTP framing or inconsistent content length cannot establish a result. Body streaming checks a monotonic request budget so a byte-dripping peer cannot keep extending the body read indefinitely. Socket-level connection/header timeouts still apply; DNS and header behavior are not a hard end-to-end real-time guarantee. Errors return INCONCLUSIVE with minimized diagnostics. See the stress validation for tested fault families and timings.

## Validation boundary

Tests exercise independent HTTP apps with healthy deletion, surviving cache entries, delayed vector reappearance, invalid/missing inventories, lost controls, transport errors, redirect refusal, credential handling and config validation. External persistence/replay/comparison are tested across restart and through CLI and official-SDK MCP client/server round trips. Optional integration tests launch actual Mem0 2.2.1 with embedded Qdrant, checking normal deletion and a deliberately injected gateway fault. Remote customer systems, production data, distributed consistency and trained learning are not validated by these tests.
