# Host memory reconciliation, protocol 1

Use the exposed MCP tools, or CLI `nuto memory scope|alias|prepare|commit|reviews`.
CLI commands take `--file <JSON-path|->` and return JSON. Keep the selected
environment/account/workspace. These transactional commands do not queue offline.
An old server keeps supporting `memory add --check`, update and supersede.

## Dimensions

`tenant_workspace_id` and `owner_user_id` are filled by authenticated context.
Never derive a tenant from a host project's workspace name. `project_key`
identifies the matter/repository; Git SSH/HTTPS remotes normalize to one key.
`host_workspace_ref` identifies a host software workspace, not the Nuto tenant.
`host_app` names software (codex, claude_code, cursor…), not a model or an agent
identity. `user_agent_id` is reserved for verified bindings and currently must
stay absent. Old arbitrary `agent_id` remains a `legacy_agent_label`.
`agent_run_id` identifies a run, not a login session. Unknowns stay absent.
Device/model provenance remains optional client metadata, never an authorization claim.

Use `scope_kind: project` with a project, `global` for a fact known to hold
across projects, `unknown` when unclassified. Give a stable `subject_ref` (who
or what the fact describes) and `aspect` (what property), reusing candidate
values. Similar spelling is not identity proof. Register only known aliases
with `memory alias` / `nuto_memory_alias`: project, host_workspace, subject,
aspect. Aliases are isolated by tenant, owner and applicable project/host.
They cannot be silently overwritten or chained. Never upload local absolute paths.
For a multi-project host workspace, register `host_workspace` aliases separately
with each `project_key`; resolving an explicitly supplied project never guesses
another project from the host workspace. An unscoped mapping is only a default.

## Prepare, judge, commit

CLI prepare request (MCP wraps it in `request`):

```json
{"drafts":[{"key":"replicas","content":"The API deployment uses two replicas.","scope":{"project_key":"github.com/team/api","host_app":"codex","subject_ref":"api-deployment","aspect":"replicas","scene":"working"},"categories":["environment"],"issues":["SQ-42"],"source_ref":"commit:abcdef"}]}
```

Prepare accepts 1–8 atomic facts. It returns `batch_id`, a 30-minute expiry,
sanitized canonical `drafts[].draft`, existing candidates (IDs, versions,
scope, dates, visibility, immutable/reviewed flags), and earlier `batch_candidates`.
Compare both existing and batch candidates. A degraded vector check can miss
paraphrases. Never interpret an empty result as certainty.

For each draft choose one operation:

| Action | Meaning |
|---|---|
| ADD | A distinct fact, subject, project or dated occurrence. |
| CONFIRM | Same fact already present; add provenance without changing its text/version. |
| NOOP | Nothing worth retaining from this draft. |
| AMEND | Same fact plus compatible detail; send the complete `merged_content`. |
| SUPERSEDE | The state changed; the earlier fact remains as history. |
| LINK | A distinct related fact; use supports, contradicts, same_series, derived_from. Contradicts is for intentional coexistence, not an unresolved state change. |
| NEEDS_REVIEW | Uncertain contradiction/identity; keep a proposal outside active recall. |

Commit resubmits the **exact sanitized drafts returned by prepare**, with
`batch_id` and one operation per draft. Existing targets need `target_id`
and `target_version` from offered candidates. A batch target uses
`target_draft_key` of an offered earlier draft already handled by the commit.
CONFIRM/AMEND/SUPERSEDE require matching project, subject/aspect, dates and
visibility; unclassified historical facts may be explicitly classified by
the host. Locks still apply. A batch error rolls everything back.

```json
{"batch_id":"<returned-id>","drafts":["<replace with returned draft objects>"],"operations":[{"draft_key":"replicas","action":"CONFIRM","target_id":"<offered-id>","target_version":1}]}
```

Retry identical commits with the same batch and arguments (and MCP
`idempotency_key`). A changed draft, changed memory scope, or expiry requires
preparing again. Never respond to a stale commit by falling back to ADD.
The CLI prints IDs/versions in the receipt; use normal memory reads for text.
`memory reviews` / `nuto_memory_reviews` lists proposals; they are not facts.

Ordinary memory reads and Snapshot/Pull also return output-only `readContext`
(schema 1): canonical scope, classification version, source count, the newest
eight source references and up to sixteen semantic links. Truncation flags say
when more exist. `reviewed` confirms classification against this content
version, not factual truth. A legacy edit keeps metadata and clears that
confirmation. An absent extension from an older server/cache means unknown.
CONFIRM reaches sync even when the content version stays the same. After
`nuto sync pull`, `memory get --offline --json` retains these fields; Secret
values remain online only. Shared memories never expose private link targets.

## Scene policies

- **chat:** long-lived preferences/facts; skip transient conversation.
- **working:** decisions, constraints, reusable experience; checkpoint task progress
  separately. Recall current project facts first, then applicable global facts.
- **coding:** short conclusions and minimal Git references (repository,
  immutable commit/base, relative file/symbol, PR/CI link). Query local Git
  on demand. Do not archive code, full diffs, logs or agent traces in cloud
  memory. Content is capped at 4000 bytes. Uncommitted work may be lost;
  retain any necessary local evidence in the host, not a cloud code dump.

Secrets never belong in draft text, labels or references. If redaction warns,
offer private Secret storage with consent; do not recover the value from chat
or report a secret save that did not occur.
