# Analisi tecnica - sviluppo `memory-node`

Data: 2026-05-05
Stato: implementazione M2 completata
Ambito: `mcp-servers` come toolbelt aziendale cross-project

Aggiornamento stato (2026-05-11):

- Milestone 1a completata: core store SQLite + tool MCP minimi.
- Milestone 1b completata: safety/audit (allowedRoots, redazione audit, isolamento cross-project, smoke stdio).
- Milestone 1c completata: search quality (ranking FTS prevedibile, snippet, verification_hint, fallback LIKE con warning quando FTS non disponibile).
- Hardening v1.1 completato: limiti input configurabili (tags/title/summary/source_ref/source_uri), toggle audit via env e copertura test/smoke su scenario degraded FTS.
- Validazione completata con test locali (`npm test` in `memory-node`) e smoke dedicato (`tests/smoke/memory-node.smoke.mjs`).

Aggiornamento stato (2026-05-12):

- Milestone 2 completata: tool tassonomia progetto (`memory_list_scopes`, `memory_list_topics`, `memory_list_tags`) e invalidazione soft (`memory_invalidate_entry`).
- Search ranking M2 completato: FTS ordina per `bm25`, poi `importance DESC`, `confidence DESC`, `updated_at DESC`; fallback LIKE mantiene gli stessi tie-break dopo `like_score`.
- Validazione M2 completata con test locali e smoke dedicato; dopo aggiornamento runtime serve riavvio del server MCP.

Validazione operativa aggiuntiva (2026-05-12, MCP diretto):

- Eseguito test end-to-end M2 tramite tool MCP `memory-node` (non smoke, non chiamate interne), simulando repository esterno con `project_path` dedicato e `project_id` esplicito.
- Flusso verificato: `memory_status` -> `memory_list_scopes/topics` -> `memory_search` baseline -> `memory_add_entry` -> `memory_read_entry` -> `memory_invalidate_entry` (idempotenza inclusa).
- Esito: comportamento conforme al contratto M2 per stato/tassonomia/search/write/read/invalidazione soft.

## 0. Evidenze test M2 via MCP diretto

Data test: 2026-05-12
Target simulato esterno: `e:\mcp-servers\tmp\memory-m2-external-project`
Isolamento progetto: `project_id = m2-ext-2026-05-12`

### 0.1 Baseline read-only

- `memory_status`: store attivo, feature `fts=true`, `audit_log=true`, contatori iniziali a zero.
- `memory_list_scopes` (status `active`): nessun elemento.
- `memory_list_topics` (status `active`): nessun elemento.
- `memory_list_tags` (status `active`): nessun elemento.
- `memory_search` (mode `project`): nessun risultato.

### 0.2 Write/read + tassonomia

- `memory_add_entry` con `source_type=agent_note`, `dedupe=true`: creata entry `0e4bb88d-d2fd-4864-a81d-a513fcd60050`.
- `memory_search` (project-mode): entry recuperata con metadati attesi (`source_type`, `source_ref`, `status`, `confidence`, `verification_hint`).
- `memory_read_entry`: contenuto e summary coerenti con input.
- `memory_list_scopes` (active): `memory-node` count `1`.
- `memory_list_topics` (active, scope `memory-node`): `m2-validation` count `1`.

### 0.3 Dedupe e lifecycle status

- `memory_add_entry` ripetuto con stesso payload e `dedupe=true`: `deduped=true`, nessuna nuova entry.
- `memory_invalidate_entry` (prima chiamata): transizione `active -> invalidated`, `changed=true`.
- `memory_invalidate_entry` (seconda chiamata): idempotenza confermata, stato invariato `invalidated`, `changed=false`.
- `memory_search` filtrato `status=active`: zero risultati.
- `memory_list_tags` filtrato `status=active`: nessun elemento.
- `memory_search` filtrato `status=invalidated`: entry presente.
- `memory_list_tags` filtrato `status=invalidated`: `direct-mcp`, `m2`, `validation` (count `1` ciascuno).
- `memory_list_tags` su `scope=memory-node`, `topic=m2-validation`, `status=invalidated`: stessi tag attesi.
- `memory_status` finale: `entries=1`, `active=0`, `invalidated=1`.

### 0.4 Note di conformita' M2

- Confermato che i tool read-only (`status`, `list_*`) non creano righe progetto in assenza di write.
- Confermato comportamento project-scoped con `project_path` esplicito e isolamento robusto con `project_id` esplicito.
- Confermata invalidazione soft idempotente.
- Copertura tassonomia completata: `memory_list_scopes`, `memory_list_topics` e `memory_list_tags` verificati su filtri `active`/`invalidated` e su filtro combinato `scope+topic`.

## 1. Sintesi decisionale

Creare un nuovo server MCP `memory-node` dedicato alla memoria operativa locale, multi-progetto e portabile, da distribuire tramite il repository `mcp-servers` insieme agli altri server MCP, skill e agenti.

Il punto chiave e' che `mcp-servers` non e' il progetto applicativo che consuma la memoria. E' un serbatoio aziendale di strumenti. `memory-node` deve quindi funzionare come tool esterno usato dagli agenti quando lavorano su repository aziendali target, sempre tramite `project_path`, `allowedRoots`, configurazione runtime e `.env` del progetto target.

Decisione proposta:

```text
memory-node = server MCP di memoria locale cross-project
```

Non deve essere:

```text
- una memoria del repository mcp-servers;
- un fork diretto di MemPalace;
- un'estensione implicita di docs-node;
- un servizio cloud;
- un database globale non filtrabile per progetto;
- un sistema che scrive nel progetto target senza richiesta esplicita.
```

La prima versione deve essere Node.js/TypeScript, coerente con lo stack del workspace, basata su SQLite/FTS5 e senza dipendenze Python obbligatorie. Il supporto vettoriale/semantico puo' arrivare in una milestone successiva tramite backend pluggabile.

## 2. Contesto del repository `mcp-servers`

Il repository `mcp-servers` e' un workspace per server MCP locali Node.js/TypeScript, skill operative e tooling di supporto. Include server come `cf-node`, `docs-node`, `git-node`, `linter-node`, `mantis-node`, `office-node`, `playwright-node`, `projectfs-node` e `sql-node`, oltre a `skills/`, `docs/`, script di setup e generatori di configurazione MCP.

Questa natura di "toolbelt" impone un vincolo architetturale:

```text
mcp-servers = runtime/toolbelt aziendale
project_path = contesto operativo reale del progetto target
```

Ogni nuovo server MCP deve quindi essere progettato per essere avviato dal client/host MCP e chiamato dagli agenti su progetti esterni, non per operare implicitamente sul repository `mcp-servers`.

## 3. Problema da risolvere

Gli agenti che lavorano su repository aziendali perdono facilmente continuita' tra sessioni, task, ticket, decisioni e convenzioni locali. Oggi alcuni strumenti coprono pezzi del problema:

- `docs-node` indicizza documentazione Markdown e la rende cercabile.
- `projectfs-node` legge in modo sicuro file del progetto target.
- `git-node` aiuta su storia Git, diff e repository.
- `mantis-node` collega ticket e allegati.
- skill e agenti codificano workflow procedurali.

Manca pero' uno strato dedicato a memorizzare conoscenza operativa appresa durante il lavoro, per esempio:

- decisioni tecniche prese in un progetto;
- spiegazioni di bug ricorrenti;
- convenzioni locali non ancora documentate;
- note di handoff tra sessioni;
- sintesi di ticket complessi;
- diario operativo di un agente;
- correlazioni tra ticket, commit, file, docs e workaround;
- "questa cosa e' gia' stata verificata e il risultato era...".

`docs-node` e `memory-node` devono rimanere distinti:

```text
docs-node
  Fonte documentale indicizzata: README, guide, documentazione ufficiale, knowledge base Markdown.

memory-node
  Memoria operativa: decisioni, fatti appresi, note sessione, diary agente, correlazioni, cache ragionata di evidenze.
```

## 4. Obiettivi

### 4.1 Obiettivi funzionali

`memory-node` deve permettere di:

1. registrare entry di memoria associate a un progetto target;
2. cercare entry per testo, scope, topic, tag e sorgente;
3. leggere una entry completa;
4. elencare scope/topic/tag disponibili per un progetto;
5. esportare/importare memoria di progetto;
6. cancellare o invalidare entry obsolete;
7. mantenere audit log delle scritture;
8. funzionare anche senza modelli LLM o API esterne;
9. restituire output brevi ma con `structuredContent` stabile;
10. restare compatibile con host MCP severi.

### 4.2 Obiettivi non funzionali

- Local-first.
- Cross-project.
- Node.js/TypeScript-first.
- Nessuna dipendenza Python obbligatoria.
- SQLite come baseline semplice e portabile.
- `.env` del progetto target con precedenza sulle env di bootstrap quando rilevante.
- Sicurezza tramite `allowedRoots`, normalizzazione path e limiti dimensionali.
- Compatibilita' Windows/Linux/macOS.
- Test e smoke su stdio.
- Documentazione e checklist PR come per gli altri server MCP.

## 5. Non-obiettivi v1

La v1 non deve includere:

- knowledge graph completo;
- ricerca vettoriale obbligatoria;
- integrazione cloud;
- sincronizzazione multiutente;
- deduplicazione semantica avanzata;
- auto-mining invasivo di intere conversazioni;
- hook automatici che scrivono memoria senza tool call esplicita;
- scrittura automatica nel repository applicativo;
- modifiche invasive a `docs-node`;
- nuova governance globale che obbliga ogni agente a usare la memoria prima di ogni risposta.

Questi punti potranno essere valutati dopo una v1 stabile.

## 6. Principi architetturali

### 6.1 Project-first, non repo-first

Ogni operazione che riguarda un progetto deve accettare `project_path`.

Esempio:

```json
{
  "project_path": "D:\\work\\cliente-x\\app",
  "scope": "backend",
  "topic": "auth-token-expiry",
  "content": "Il middleware legacy usa scadenza token inclusiva; usare < e non <= nei nuovi fix."
}
```

Il server puo' avere uno store fisico globale locale, ma ogni query deve essere filtrabile per progetto. Il comportamento predefinito vincolante e':

- se `project_path` e' presente, lavorare nel namespace del progetto target;
- se `project_path` manca ma `project_id` e' presente, lavorare nel namespace di quel progetto;
- se mancano sia `project_path` sia `project_id`, rifiutare le operazioni project-scoped;
- usare `mode="cross_project"` come unico meccanismo per ricerche o letture cross-project esplicite;
- evitare che una query generica mischi accidentalmente memorie di piu' clienti/progetti.

### 6.2 Identita' progetto stabile

`project_path` e' utile ma non sufficiente come identificativo permanente, perche' lo stesso repository puo' essere clonato in path diversi.

Proposta:

```text
project_id = valore derivato e stabile, con override esplicito
```

Risoluzione consigliata:

1. campo `project_id` passato al tool;
2. `MEMORY_PROJECT_ID` dal `.env` del progetto target;
3. remote Git canonicalizzato, se leggibile;
4. fallback: hash normalizzato del `project_path`.

Esempi:

```text
sophia/customer-portal
ai-documents-validation
cliente-x/app
git:https://github.com/company/repo.git
pathhash:6f4d...
```

### 6.3 Store globale, namespace per progetto

Lo store fisico puo' essere uno solo per utente/macchina:

```text
~/.mcp-servers/memory/memory.sqlite
```

oppure configurabile:

```dotenv
MEMORY_DB_PATH=D:\mcp-runtime\memory\memory.sqlite
```

La separazione logica avviene tramite `project_id`.

Questo permette ricerca cross-project solo quando esplicitamente richiesta, per esempio:

```json
{
  "query": "pattern token expiry legacy",
  "mode": "cross_project",
  "limit": 10
}
```

### 6.4 Verbatim-first

Come principio ispirato a sistemi di memoria locale tipo MemPalace, la v1 deve salvare testo originale o note dichiaratamente prodotte dall'agente senza trasformazioni opache.

Regola:

```text
content = testo originale o nota esplicita
summary = opzionale, separato dal contenuto
metadata = tracciabilita'
```

Non sovrascrivere il contenuto originale con riassunti.

### 6.5 Docs-first resta separato

`memory-node` non sostituisce la policy docs-first. Quando la domanda riguarda documentazione ufficiale del progetto, `docs-node` resta fonte primaria. `memory-node` puo' fornire:

- note operative;
- decisioni;
- contesto storico;
- punti aperti;
- handoff;
- evidenze raccolte in sessioni precedenti.

Il tool dovrebbe distinguere nei metadati:

```text
source_type:
  agent_note
  decision
  handoff
  ticket_summary
  commit_context
  doc_observation
  file_observation
  bug_pattern
  workaround
  user_preference
  environment_note
```

## 7. Relazione con strumenti esistenti

### 7.1 `docs-node`

`docs-node` indicizza Markdown in SQLite/FTS5, gestisce shelf, documenti, ricerca e import/export. `memory-node` puo' riusare alcuni pattern concettuali:

- SQLite;
- ricerca full-text;
- export/import;
- risorse URI;
- output strutturato;
- action pattern, dove opportuno.

Non deve pero' condividere automaticamente il database di `docs-node` nella v1. La separazione riduce rischio e permette evoluzione indipendente.

Possibile integrazione futura:

```text
memory_add_from_docs_result
memory_link_doc
memory_search_with_docs_context
```

### 7.2 `projectfs-node`

`projectfs-node` e' il riferimento per:

- `allowedRoots`;
- `project_path`;
- precedenza `.env` del progetto target;
- policy filesystem sicure;
- read-only discipline.

`memory-node` deve adottare lo stesso spirito:

- normalizzazione path;
- rifiuto path fuori whitelist per sorgenti file;
- limiti dimensionali;
- nessun follow link pericoloso;
- configurazione bootstrap sovrascrivibile dal progetto target dove applicabile.

### 7.3 `git-node`

`memory-node` puo' salvare riferimenti a commit, branch, PR e diff, ma non deve duplicare le funzioni di `git-node`.

Esempio entry:

```json
{
  "source_type": "commit_context",
  "source_ref": "git:commit:abc123",
  "content": "Il commit abc123 ha introdotto fallback X per compatibilita' con ambiente Y."
}
```

### 7.4 `mantis-node`

Per ticket Mantis, `memory-node` puo' salvare sintesi operative e decisioni derivate da analisi ticket/allegati, mantenendo riferimento esterno:

```json
{
  "source_type": "ticket_summary",
  "source_ref": "mantis:12345",
  "content": "Il ticket riguarda regressione in export XLS; root cause gia' isolata nel mapper colonne."
}
```

### 7.5 Skill e agenti

Le skill possono usare `memory-node` come supporto, ma non deve diventare una dipendenza obbligatoria per ogni workflow.

Esempi:

- `mcp-technical-analyst`: salva handoff e ricostruzioni multi-sorgente.
- `mcp-master-orchestrator`: salva stato di avanzamento tra fasi.
- skill specialistiche: salvano convenzioni specifiche apprese nel progetto target.

## 8. Modello dati v1

### 8.1 Entita' principali

```text
projects
memory_entries
memory_tags
memory_entry_tags
memory_links
audit_log
schema_migrations
```

### 8.2 Tabella `projects`

```sql
CREATE TABLE projects (
  id TEXT PRIMARY KEY,
  canonical_path TEXT,
  display_name TEXT,
  git_remote TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  metadata_json TEXT
);
```

Note:

- `id` e' `project_id`.
- `canonical_path` e' utile ma non unico.
- `git_remote` aiuta a riconoscere cloni diversi.
- `metadata_json` puo' contenere linguaggio, framework, cliente, ambiente.

### 8.3 Tabella `memory_entries`

```sql
CREATE TABLE memory_entries (
  id TEXT PRIMARY KEY,
  project_id TEXT NOT NULL,
  scope TEXT NOT NULL,
  topic TEXT NOT NULL,
  title TEXT,
  content TEXT NOT NULL,
  summary TEXT,
  source_type TEXT NOT NULL,
  source_ref TEXT,
  source_path TEXT,
  source_uri TEXT,
  content_sha256 TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'active',
  confidence REAL,
  importance INTEGER DEFAULT 3,
  created_by TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  invalidated_at TEXT,
  invalidation_reason TEXT,
  metadata_json TEXT,
  FOREIGN KEY(project_id) REFERENCES projects(id)
);
```

Valori consigliati per `status`:

```text
active
superseded
invalidated
archived
```

Valori consigliati per `importance`:

```text
1 = bassa
2 = utile
3 = normale
4 = importante
5 = critica
```

### 8.4 Full-text index

Decisione v1: usare una tabella FTS esterna con `entry_id` esplicito, invece di affidare il contratto pubblico al `rowid` SQLite. Questo evita ambiguita' con `id TEXT PRIMARY KEY`, semplifica import/export futuri e rende piu' chiaro il rebuild dell'indice.

```sql
CREATE VIRTUAL TABLE memory_entries_fts USING fts5(
  entry_id UNINDEXED,
  title,
  content,
  summary,
  scope,
  topic
);
```

La v1 deve mantenere `memory_entries_fts` con trigger o aggiornamenti transazionali applicativi su insert/update/delete. In caso di FTS non disponibile nella build SQLite, il server puo' degradare a `LIKE` con warning in `memory_status.features`, ma lo smoke principale deve coprire FTS quando disponibile.

### 8.5 Tag

```sql
CREATE TABLE memory_tags (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  project_id TEXT NOT NULL,
  name TEXT NOT NULL,
  description TEXT,
  created_at TEXT NOT NULL,
  UNIQUE(project_id, name)
);

CREATE TABLE memory_entry_tags (
  entry_id TEXT NOT NULL,
  tag_id INTEGER NOT NULL,
  PRIMARY KEY(entry_id, tag_id),
  FOREIGN KEY(entry_id) REFERENCES memory_entries(id),
  FOREIGN KEY(tag_id) REFERENCES memory_tags(id)
);
```

### 8.6 Link tra entry

```sql
CREATE TABLE memory_links (
  id TEXT PRIMARY KEY,
  project_id TEXT NOT NULL,
  source_entry_id TEXT NOT NULL,
  target_entry_id TEXT NOT NULL,
  relation TEXT NOT NULL,
  label TEXT,
  created_at TEXT NOT NULL,
  metadata_json TEXT,
  FOREIGN KEY(source_entry_id) REFERENCES memory_entries(id),
  FOREIGN KEY(target_entry_id) REFERENCES memory_entries(id)
);
```

Relazioni v1:

```text
related_to
supersedes
derived_from
blocks
explains
duplicates
```

### 8.7 Audit log

Due livelli:

1. tabella interna `audit_log`;
2. JSONL append-only opzionale su filesystem.

```sql
CREATE TABLE audit_log (
  id TEXT PRIMARY KEY,
  timestamp TEXT NOT NULL,
  project_id TEXT,
  operation TEXT NOT NULL,
  entry_id TEXT,
  actor TEXT,
  params_redacted_json TEXT,
  result_json TEXT
);
```

File opzionale:

```text
~/.mcp-servers/memory/audit/memory-write-log.jsonl
```

Contenuti sensibili come `content`, `query`, `text`, `document` vanno redatti o sostituiti da hash/lunghezza.

Contratto minimo di redazione:

- non salvare mai `content`, `summary`, `query`, `text`, `document` o payload file raw in `params_redacted_json`;
- salvare al loro posto `sha256`, lunghezza byte/caratteri e nome campo;
- mantenere `operation`, `project_id`, `entry_id`, `actor`, `timestamp`, esito e codici errore;
- evitare stack trace grezzi in `result_json`;
- applicare la stessa redazione al JSONL opzionale.

## 9. Configurazione

### 9.1 Env globali

```dotenv
MEMORY_DB_PATH=
MEMORY_ALLOWED_ROOTS=
MEMORY_CONFIG=
MEMORY_MAX_ENTRY_BYTES=200000
MEMORY_MAX_SEARCH_RESULTS=50
MEMORY_AUDIT_LOG=1
MEMORY_MAX_TAGS=20
MEMORY_MAX_TITLE_CHARS=200
MEMORY_MAX_SUMMARY_CHARS=500
MEMORY_MAX_SOURCE_REF_CHARS=400
MEMORY_MAX_SOURCE_URI_CHARS=1000
MEMORY_MAX_PROJECT_ID_CHARS=200
MEMORY_MAX_SCOPE_CHARS=120
MEMORY_MAX_TOPIC_CHARS=200
MEMORY_MAX_TAG_CHARS=80
MEMORY_ENABLE_VECTOR=0
MEMORY_VECTOR_BACKEND=none
MEMORY_DEBUG=0
```

Default runtime corrente: se `MEMORY_DB_PATH` non e' impostato, il server usa lo store utente
`~/.mcp-servers/memory/memory.sqlite`, evitando di creare memoria implicita nel repo toolbelt.

Nota terminologica:

- `MEMORY_ALLOWED_ROOTS` e' la variabile env;
- `allowedRoots` e' il nome della configurazione interna, allineato a `projectfs-node`;
- evitare `allowed_roots` negli input schema pubblici salvo motivazione esplicita.

### 9.2 Env del progetto target

Nel `.env` del progetto applicativo:

```dotenv
MEMORY_PROJECT_ID=cliente-x/app
MEMORY_PROJECT_DISPLAY_NAME=Cliente X App
MEMORY_SCOPE_DEFAULT=backend
MEMORY_ALLOWED_TAGS=decision,bug,workaround,auth,db,ui
```

### 9.3 Precedenza configurazione

Allinearsi al pattern di `projectfs-node`:

1. `.env` del `project_path` passato al tool;
2. `.env` locale scoperto automaticamente, se previsto;
3. env `MEMORY_*` del launcher/host;
4. file `memory.config.json`;
5. default interni.

Importante: le env di dominio del progetto target devono poter sovrascrivere il bootstrap launcher.

## 10. Superficie MCP proposta

### 10.1 Convenzioni generali

- Usare `Server` low-level + `setRequestHandler`, coerente con molti server esistenti.
- Ogni tool con `inputSchema` strict e `items` su tutti gli array.
- Output duale:
  - `content`: testo breve leggibile;
  - `structuredContent`: oggetto stabile.
- Tool read-only marcati con annotations read-only.
- Tool write con hint coerenti e audit log.
- Errori in forma MCP compatibile, non stack trace grezzi.

### 10.2 Tool proposti (roadmap)

Per evitare ambiguita' di scope:

- **Milestone 1 (core v1 minima)**: `memory_status`, `memory_add_entry`, `memory_search`, `memory_read_entry`.
- **Milestone successive**: i tool elencati dopo il core (list/invalidate/update/delete/import/export e altri opzionali).

### 10.2.1 Contratto Milestone 1

La prima PR comportamentale deve restare deliberatamente stretta. Il contratto M1 e':

```text
Tool inclusi:
  memory_status
  memory_add_entry
  memory_search
  memory_read_entry

Esclusi da M1:
  update/delete/import/export/resources/prompts/link/semantic-vector
```

Regole di input:

- ogni operazione project-scoped richiede `project_path` o `project_id`;
- `memory_add_entry` richiede `project_path`, salvo futura decisione esplicita su scritture globali;
- `mode` accetta solo `project` o `cross_project`;
- default `mode="project"`;
- `mode="cross_project"` deve essere passato esplicitamente;
- nessun flag alternativo tipo `global_scope` nella v1.

Shape minima di errore in `structuredContent`:

```json
{
  "ok": false,
  "error_code": "INVALID_PARAM",
  "detail": "...",
  "project_id": "..."
}
```

Shape minima di successo:

```json
{
  "ok": true,
  "project_id": "...",
  "warnings": []
}
```

Ogni risultato di search/read deve includere i metadati che ricordano all'agente che la memoria e' contesto operativo verificabile, non fonte autoritativa:

```text
source_type
source_ref
status
confidence
created_at
updated_at
verification_hint
```

#### `memory_status`

Scopo: stato store, progetto risolto, conteggi, feature abilitate.

Input:

```json
{
  "project_path": "string?",
  "project_id": "string?"
}
```

Output strutturato:

```json
{
  "ok": true,
  "db_path": "...",
  "project": {
    "id": "...",
    "display_name": "...",
    "canonical_path": "..."
  },
  "counts": {
    "entries": 120,
    "active": 110,
    "invalidated": 3,
    "archived": 7,
    "superseded": 0
  },
  "features": {
    "fts": true,
    "vector": false,
    "audit_log": true
  }
}
```

`memory_status` e' read-only: risolve il progetto ma non crea/aggiorna righe in `projects`.

#### `memory_add_entry`

Scopo: aggiungere o aggiornare una memoria operativa.

Input:

```json
{
  "project_path": "string",
  "project_id": "string?",
  "scope": "string",
  "topic": "string",
  "title": "string?",
  "content": "string",
  "summary": "string?",
  "source_type": "agent_note|decision|handoff|ticket_summary|commit_context|doc_observation|file_observation|bug_pattern|workaround|user_preference|environment_note",
  "source_ref": "string?",
  "source_path": "string?",
  "source_uri": "string?",
  "tags": ["string"],
  "importance": 3,
  "confidence": 0.8,
  "dedupe": true
}
```

Comportamento:

- valida `project_path`;
- risolve `project_id`;
- normalizza `scope`, `topic`, `tags`;
- calcola `content_sha256`;
- se `dedupe=true`, evita doppioni esatti nello stesso progetto;
- registra audit log;
- restituisce `entry_id`.

#### `memory_search`

Scopo: cercare memoria operativa.

Input:

```json
{
  "project_path": "string?",
  "project_id": "string?",
  "query": "string",
  "scope": "string?",
  "topic": "string?",
  "tags": ["string"],
  "source_type": "string?",
  "status": "active",
  "mode": "project|cross_project",
  "limit": 10,
  "include_content": false
}
```

Default:

- `mode="project"` se c'e' `project_path` o `project_id`;
- cross-project solo se esplicito;
- `include_content=false` per ridurre token.

Output:

```json
{
  "results": [
    {
      "id": "...",
      "project_id": "...",
      "scope": "backend",
      "topic": "auth",
      "title": "...",
      "snippet": "...",
      "tags": ["decision"],
      "source_type": "decision",
      "source_ref": "mantis:12345",
      "status": "active",
      "confidence": 0.8,
      "importance": 4,
      "created_at": "...",
      "updated_at": "...",
      "verification_hint": "Contesto operativo: verificare su docs/code/ticket prima di trattarlo come fonte attuale."
    }
  ]
}
```

#### `memory_read_entry`

Scopo: leggere contenuto completo di una entry.

Input:

```json
{
  "project_path": "string?",
  "project_id": "string?",
  "entry_id": "string",
  "mode": "project|cross_project"
}
```

Deve verificare che l'entry appartenga al progetto risolto, salvo lettura cross-project esplicita.

Default consigliato:

- `mode="project"` se omesso;
- `mode="cross_project"` solo se richiesto esplicitamente.

#### `memory_list_scopes`

Input:

```json
{
  "project_path": "string?",
  "project_id": "string?",
  "status": "active|invalidated|archived|superseded?"
}
```

Output M2: `{ ok, project_id, warnings, status, items: [{ name, count }] }`.

Default: `status="active"`.

#### `memory_list_topics`

Input:

```json
{
  "project_path": "string?",
  "project_id": "string?",
  "status": "active|invalidated|archived|superseded?",
  "scope": "string?"
}
```

Output M2: `{ ok, project_id, warnings, status, scope?, items: [{ name, count }] }`.

Default: `status="active"`.

#### `memory_list_tags`

Input:

```json
{
  "project_path": "string?",
  "project_id": "string?",
  "status": "active|invalidated|archived|superseded?",
  "scope": "string?",
  "topic": "string?"
}
```

Output M2: `{ ok, project_id, warnings, status, scope?, topic?, items: [{ name, count }] }`.

Default: `status="active"`.

Per i tre tool di listing, almeno uno tra `project_path` e `project_id` e' richiesto a runtime.

#### `memory_update_entry`

Scopo: aggiornare metadati o contenuto. Non necessario in primissima v1 se `memory_add_entry` supporta upsert controllato, ma consigliato per chiarezza.

Target roadmap: **post-Milestone 1**.

Input:

```json
{
  "project_path": "string",
  "entry_id": "string",
  "title": "string?",
  "content": "string?",
  "summary": "string?",
  "scope": "string?",
  "topic": "string?",
  "tags": ["string"]?,
  "importance": 4,
  "confidence": 0.9
}
```

#### `memory_invalidate_entry`

Scopo: marcare una entry come non piu' valida senza cancellarla.

Input:

```json
{
  "project_path": "string?",
  "project_id": "string?",
  "entry_id": "string",
  "reason": "string?"
}
```

Questo e' preferibile a delete per decisioni storiche.

Stato: **Milestone 2 completata**.

Output M2: `{ ok, project_id, warnings, entry_id, previous_status, status, invalidated_at, changed }`.

Almeno uno tra `project_path` e `project_id` e' richiesto a runtime. Il tool e' idempotente: una entry gia' invalidata torna `changed=false`.

#### `memory_delete_entry`

Scopo: cancellazione esplicita, da usare con cautela.

Input:

```json
{
  "project_path": "string",
  "entry_id": "string",
  "confirm": true
}
```

Richiedere `confirm=true`.

Target roadmap: **post-Milestone 1**.

#### `memory_export_project`

Scopo: esportare memoria di progetto su file.

Input:

```json
{
  "project_path": "string",
  "project_id": "string?",
  "save_path": "string",
  "format": "json|zip",
  "include_archived": false,
  "include_invalidated": false
}
```

Target roadmap: **Milestone 3**.

#### `memory_import_project`

Scopo: importare memoria di progetto.

Input:

```json
{
  "project_path": "string",
  "project_id": "string?",
  "import_path": "string",
  "on_conflict": "fail|merge|replace",
  "confirm_replace": false
}
```

`replace` deve richiedere `confirm_replace=true`.

Target roadmap: **Milestone 3**.

### 10.3 Tool v2 opzionali

```text
memory_link_entries
memory_list_entry_links
memory_unlink_entries
memory_mine_project_notes
memory_add_from_ticket
memory_add_from_git_context
memory_find_similar
memory_rebuild_index
```

## 11. Resources MCP

La v1 puo' esporre resources se il costo e' basso.

URI proposte:

```text
memory://project/{project_id}
memory://entry/{entry_id}
memory://project/{project_id}/scope/{scope}
memory://project/{project_id}/topic/{topic}
```

Regole:

- URI stabili e parseabili;
- non esporre contenuti enormi in `resources/list`;
- `resources/read` puo' restituire entry completa;
- mantenere sempre tool legacy equivalenti per host che non usano resources.

## 12. Prompt MCP

Prompt MCP opzionali per discoverability:

```text
memory_project_brief
memory_handoff
memory_decision_log
```

Esempio `memory_handoff`:

Input:

```json
{
  "project_path": "string",
  "since": "7d",
  "scope": "string?"
}
```

Output: prompt che guida l'agente a cercare memoria rilevante e produrre handoff.

Non sostituire le skill con prompt complessi. I prompt sono ingresso rapido, le skill restano workflow robusti.

## 13. Sicurezza e privacy

### 13.1 Rischi principali

- memorizzazione accidentale di segreti;
- contaminazione cross-project;
- leak tra clienti/progetti;
- path traversal;
- ingestion di file fuori root;
- cancellazioni distruttive;
- memoria obsoleta usata come verita' attuale;
- prompt injection salvata in memoria;
- crescita incontrollata del database.

### 13.2 Mitigazioni v1

1. Redazione audit log per contenuti sensibili.
2. `project_id` obbligatorio o risolto in modo trasparente.
3. Cross-project search disattivata di default.
4. `allowedRoots` per validare `project_path` e `source_path`.
5. Limiti:
   - `MEMORY_MAX_ENTRY_BYTES`;
   - `MEMORY_MAX_SEARCH_RESULTS`;
  - `MEMORY_MAX_TAGS`;
  - `MEMORY_MAX_TITLE_CHARS`;
  - `MEMORY_MAX_SUMMARY_CHARS`;
  - `MEMORY_MAX_SOURCE_REF_CHARS`;
  - `MEMORY_MAX_SOURCE_URI_CHARS`.
6. `status` e invalidazione invece di hard delete.
7. `confirm=true` per delete e replace.
8. Nessuna esecuzione di contenuti salvati.
9. Nessuna scrittura nel progetto target salvo export esplicito con `save_path`.
10. Warnings quando si legge memoria invalidata o archiviata.

### 13.3 Pattern "memoria non e' fonte assoluta"

Ogni risultato deve rendere visibili almeno:

```text
source_type
source_ref
created_at
updated_at
confidence
status
verification_hint
```

Gli agenti devono trattare la memoria come contesto operativo verificabile, non come verita' autoritativa. Per documentazione ufficiale, usare `docs-node`; per codice corrente, usare `projectfs-node`/`git-node`; per DB, usare `sql-node`.

`verification_hint` deve essere breve e stabile. Esempio:

```text
Contesto operativo: verificare su docs/code/ticket prima di trattarlo come fonte attuale.
```

## 14. Ricerca v1

### 14.1 FTS baseline

La ricerca v1 usa SQLite FTS5 con:

- query testo;
- filtro `project_id`;
- filtro `scope`;
- filtro `topic`;
- filtro `tags`;
- filtro `source_type`;
- filtro `status`;
- ordinamento misto:
  - ranking FTS;
  - `importance`;
  - freschezza;
  - status active.

### 14.2 Ranking suggerito

Score finale euristico:

```text
score = fts_rank_weight
      + importance_boost
      + freshness_boost
      + exact_scope_topic_boost
      - archived_penalty
      - invalidated_penalty
```

Non serve perfezione nella v1; serve prevedibilita'.

### 14.3 Snippet

Gli snippet devono evidenziare match senza restituire l'intero contenuto. Default `include_content=false`.

### 14.4 Search discipline

Quando `memory_search` non trova risultati:

- restituire zero risultati, non inventare;
- suggerire all'agente di consultare docs/code/ticket;
- opzionalmente indicare scope/topic disponibili se utili.

## 15. Backend vettoriale futuro

### 15.1 Perche' non in v1

La ricerca vettoriale migliora recall semantico, ma aumenta complessita':

- modello embedding locale;
- dimensioni vettori;
- rebuild index;
- dipendenze native;
- performance;
- compatibilita' Windows/Linux;
- packaging.

Meglio rilasciare prima FTS stabile.

### 15.2 Interfaccia futura

```ts
export interface VectorBackend {
  name: string;
  init(): Promise<void>;
  upsert(entries: VectorEntry[]): Promise<void>;
  delete(entryIds: string[]): Promise<void>;
  search(query: string, filters: MemoryFilters, limit: number): Promise<VectorHit[]>;
  health(): Promise<HealthStatus>;
}
```

Backend possibili:

```text
none
sqlite-vec
sqlite-vss
lancedb
qdrant-local
custom-http
```

### 15.3 Migrazione

Aggiungere tabella:

```sql
CREATE TABLE memory_vectors (
  entry_id TEXT PRIMARY KEY,
  backend TEXT NOT NULL,
  model TEXT NOT NULL,
  dimensions INTEGER NOT NULL,
  vector_blob BLOB,
  updated_at TEXT NOT NULL
);
```

## 16. Struttura file proposta

```text
memory-node/
  package.json
  tsconfig.json
  README.md
  src/
    index.ts
    config.ts
    env.ts
    db.ts
    migrations.ts
    schema.ts
    tools.ts
    search.ts
    project-resolver.ts
    path-policy.ts
    audit.ts
    responses.ts
    resources.ts
    prompts.ts
    errors.ts
  test/
    config.test.mjs
    project-resolver.test.mjs
    memory-tools.test.mjs
    search.test.mjs
    import-export.test.mjs
```

Smoke:

```text
tests/smoke/memory-node.smoke.mjs
```

Docs:

```text
docs/memory-node/memory-node-technical-analysis.md
memory-node/README.md
```

## 17. Package e dipendenze

### 17.1 `package.json`

```json
{
  "name": "memory-node",
  "version": "0.1.0",
  "description": "Cross-project local memory MCP server for operational agent context",
  "main": "dist/index.js",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "test": "node --test test/*.test.mjs"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.26.0",
    "sqlite3": "^6.0.1",
    "dotenv": "^16.4.7"
  },
  "devDependencies": {
    "@types/node": "^25.2.3",
    "tsx": "^4.21.0",
    "typescript": "^5.9.3"
  }
}
```

Nota: valutare `better-sqlite3` solo se gia' accettabile nel workspace. `sqlite3` e' coerente con `docs-node`.

### 17.2 Node target

Allinearsi al target workspace Node LTS 24.x. Evitare API sperimentali non necessarie.

## 18. Implementazione: componenti principali

### 18.1 `config.ts`

Responsabilita':

- leggere env;
- caricare `memory.config.json`;
- applicare precedenza;
- validare limiti;
- risolvere path DB;
- preparare `allowedRoots`.

### 18.2 `project-resolver.ts`

Responsabilita':

- validare `project_path`;
- normalizzare path;
- leggere `.env` target;
- derivare `project_id`;
- leggere remote Git se disponibile;
- upsert tabella `projects`.

Pseudocodice:

```ts
async function resolveProject(args, config): Promise<ProjectContext> {
  const projectPath = normalizeOptionalPath(args.project_path);
  const targetEnv = projectPath ? loadProjectEnv(projectPath) : {};
  const projectId =
    args.project_id ||
    targetEnv.MEMORY_PROJECT_ID ||
    await deriveFromGitRemote(projectPath) ||
    hashPath(projectPath);

  return {
    projectId,
    projectPath,
    displayName: targetEnv.MEMORY_PROJECT_DISPLAY_NAME || basename(projectPath),
    env: targetEnv
  };
}
```

La priorita' sopra e' intenzionale: input tool esplicito > `.env` del progetto target.

### 18.3 `path-policy.ts`

Responsabilita':

- controllare `allowedRoots`;
- impedire traversal;
- normalizzare Windows/Linux;
- validare `source_path`, `save_path`, `import_path`;
- non seguire symlink fuori whitelist.

### 18.4 `db.ts` / `migrations.ts`

Responsabilita':

- apertura SQLite;
- init schema;
- migration versionate;
- FTS trigger;
- transazioni.

Migrazione iniziale:

```text
001_init_memory_schema
```

### 18.5 `tools.ts`

Responsabilita':

- definire `Tool[]`;
- schema input;
- routing `CallToolRequestSchema`;
- delega a funzioni applicative;
- error handling.

### 18.6 `responses.ts`

Responsabilita':

- `content` breve;
- `structuredContent`;
- error shape uniforme;
- warning per entry obsolete.

### 18.7 `audit.ts`

Responsabilita':

- redazione parametri;
- scrittura tabella audit;
- scrittura JSONL opzionale;
- hash contenuti.

## 19. Schema MCP e compatibilita' host severi

Checklist per ogni tool:

```text
- inputSchema.type = "object"
- properties esplicite
- required coerente
- array sempre con items
- enum quando possibile
- description chiare ma non prolisse
- niente oneOf/anyOf complessi se evitabili
- output structuredContent stabile
```

Annotations:

```ts
const READ_ONLY_ANNOTATIONS = {
  readOnlyHint: true,
  destructiveHint: false,
  idempotentHint: true,
  openWorldHint: false
};

const WRITE_ANNOTATIONS = {
  readOnlyHint: false,
  destructiveHint: false,
  idempotentHint: false,
  openWorldHint: false
};

const DESTRUCTIVE_ANNOTATIONS = {
  readOnlyHint: false,
  destructiveHint: true,
  idempotentHint: false,
  openWorldHint: false
};
```

`memory_delete_entry` e import replace devono essere destructive.

## 20. Import/export

### 20.1 Export JSON

Formato:

```json
{
  "format": "memory-node.project-export",
  "version": 1,
  "exported_at": "2026-05-05T00:00:00.000Z",
  "project": {
    "id": "cliente-x/app",
    "display_name": "Cliente X App"
  },
  "entries": [],
  "tags": [],
  "links": []
}
```

### 20.2 Export ZIP

Contenuto:

```text
manifest.json
entries.json
tags.json
links.json
audit-summary.json
```

Non includere audit completo per default.

### 20.3 Import conflict policy

```text
fail
  fallisce se entry gia' presenti.

merge
  inserisce nuove entry e aggiorna metadati non distruttivi.

replace
  cancella memoria progetto e reimporta; richiede confirm_replace=true.
```

## 21. Integrazione setup workspace

Stato attuale (2026-05-11): integrazione v1 completata.

- `setup.ps1` e `setup.sh`: `memory-node` incluso automaticamente dal discovery `package.json` top-level.
- `genera_mcp_json.ps1` e `genera_mcp_json.sh`: includono `memory-mcp-server` nei file `settings.*` generati.
- `scripts/inject-user-mcp-settings.js`: merge Codex/VS Code con hardening su path Windows (backslash) in fase di inject.
- `tests/smoke/run-all.mjs`: include `memory-node.smoke.mjs`.
- documentazione root e capability matrix allineate al wiring runtime.

Aggiornare:

```text
README.md
setup.ps1
setup.sh
genera_mcp_json.ps1
genera_mcp_json.sh
scripts/inject-user-mcp-settings.js
tests/smoke/run-all.mjs
docs/server-capability-matrix.md
docs/mcp-skills-agents-development-guide.md
```

Esempio config VS Code/Copilot:

```json
{
  "servers": {
    "memory-mcp-server": {
      "command": "C:\\Program Files\\nodejs\\node.exe",
      "args": ["D:\\mcp-servers\\memory-node\\dist\\index.js"],
      "env": {
        "MEMORY_DB_PATH": "D:\\mcp-runtime\\memory\\memory.sqlite",
        "MEMORY_ALLOWED_ROOTS": ""
      }
    }
  }
}
```

Esempio Codex:

```toml
[mcp_servers.memory-mcp-server]
command = 'C:\Program Files\nodejs\node.exe'
args = ['D:\mcp-servers\memory-node\dist\index.js']
enabled = true

[mcp_servers.memory-mcp-server.env]
MEMORY_DB_PATH = 'D:\mcp-runtime\memory\memory.sqlite'
MEMORY_ALLOWED_ROOTS = ''
```

## 22. Workflow agentico consigliato

### 22.1 Inizio task

1. Identificare `project_path`.
2. Leggere eventuale `AGENTS.md` del progetto target.
3. Usare docs/code/ticket come fonti primarie.
4. Usare `memory_search` per contesto operativo precedente se il task lo giustifica.

### 22.2 Durante analisi

Salvare memoria solo quando c'e' valore riusabile:

```text
- decisione tecnica
- root cause confermata
- workaround validato
- convenzione locale
- vincolo ambiente
- handoff multi-fase
```

Non salvare:

```text
- ogni singolo pensiero
- output temporanei
- dati sensibili
- stack trace lunghi senza valore
- contenuti duplicati della documentazione ufficiale
```

### 22.3 Fine task

Possibile handoff:

```json
{
  "source_type": "handoff",
  "scope": "task",
  "topic": "ticket-12345-export-xls",
  "title": "Handoff ticket 12345 export XLS",
  "content": "Root cause..., file toccati..., validazioni fatte..., rischi residui..."
}
```

## 23. Skill collegate (v1)

Skill introdotta e collegata alla v1:

```text
skills/mcp-memory-operator/
  SKILL.md
  references/
    when-to-save-memory.md
    memory-hygiene.md
    project-handoff.md
  evals/
    evals.json
```

Scopo skill:

- decidere quando salvare memoria;
- evitare spam;
- evitare dati sensibili;
- distinguere docs ufficiali da memoria operativa;
- produrre handoff sintetici.

Sinergie principali nel repository:

- `mcp-technical-analyst`: usa `mcp-memory-operator` per consolidare evidenze e decisioni verificabili tra sessioni.
- `mcp-master-orchestrator`: delega `mcp-memory-operator` per checkpoint operativi e handoff multi-fase.
- skill specialistiche (`mcp-coldfusion-developer`, `mcp-sophia-yii-developer`, `mcp-database-expert`, `mcp-git-mantis-workflow`): persistono convenzioni e root cause gia' validate senza duplicare documentazione ufficiale.

## 24. Test strategy

### 24.1 Unit test

```text
config.test.mjs
  precedenza env/config/default
  parsing allowed roots
  limiti default

project-resolver.test.mjs
  project_id da argomento
  project_id da env target
  fallback hash path
  path non valido

path-policy.test.mjs
  path dentro root
  path fuori root
  traversal
  Windows path normalization

memory-tools.test.mjs
  add/read/search
  dedupe esatto
  isolamento cross-project (default project)
  accesso cross-project solo con `mode="cross_project"`
  invalidate
  list scopes/topics/tags
  ranking tie-break importance/confidence

Milestone 3+ (estensioni):
  delete confirm

audit.test.mjs
  redazione campi sensibili (`content`, `query`, `text`, `document`)
  persistenza hash/lunghezza al posto del payload sensibile

import-export.test.mjs
  export json
  import fail/merge/replace
```

### 24.2 Smoke test stdio

`tests/smoke/memory-node.smoke.mjs` deve validare:

1. avvio stdio senza crash;
2. handshake MCP;
3. `tools/list`;
4. `memory_status`;
5. `memory_add_entry` su progetto temporaneo;
6. `memory_search`;
7. `memory_read_entry`;
8. isolamento cross-project di default su read/search;
9. errore path fuori root;
10. lettura cross-project consentita solo in modalita' esplicita;
11. verifica redazione audit su operazioni di write;
12. `memory_list_scopes`, `memory_list_topics`, `memory_list_tags`;
13. `memory_invalidate_entry` e ricerca per `status="invalidated"`;
14. chiusura pulita.

### 24.3 Schema validation

Controlli statici:

- ogni array ha `items`;
- required validi;
- tool names univoci;
- annotations coerenti;
- no enum incoerenti.

### 24.4 Regression test

Creare fixture temporanea:

```text
tmp/
  target-project/
    .env
    AGENTS.md
    README.md
```

Impostare `MEMORY_ALLOWED_ROOTS` alla fixture.

## 25. Roadmap proposta

### Milestone 0 - Analisi e skeleton

Output:

```text
docs/memory-node/memory-node-technical-analysis.md
memory-node/package.json
memory-node/tsconfig.json
memory-node/src/index.ts skeleton
```

Questa sezione M0 e' mantenuta come riferimento storico del bootstrap iniziale.

#### Contratto operativo M0 (storico)

Il contratto operativo della Milestone 0 e' deliberatamente limitato a preparazione e documentazione minima, senza attivazione runtime.

Vincoli M0:

- `memory-node` e' presente come skeleton (analisi tecnica + struttura iniziale package/tsconfig/index).
- nessun wiring automatico nei runtime settings client (Codex/VS Code/Copilot/Claude) in questa milestone.
- nessuna modifica a generatori settings, smoke test, setup script o capability matrix in M0.
- nessuna esposizione di nuovi tool runtime da considerare "operativi" lato utenti finali.
- compatibilita' legacy invariata per i server gia' esistenti.

Checklist M0 lato documentazione:

- analisi tecnica `memory-node` presente e coerente con guardrail `project_path`/`allowedRoots`/compatibilita' host severi.
- README root aggiornato in modo informativo: `memory-node` dichiarato come skeleton M0 non ancora wired.
- confini tra M0 (skeleton) e M1+ (comportamento runtime) esplicitati in modo verificabile.

### Milestone 1 - Store SQLite + tool minimi

Stato milestone: completata.

Checklist completamento:

- [x] M1a - Core store.
- [x] M1b - Safety and audit.
- [x] M1c - Search quality.

Tool:

```text
memory_status
memory_add_entry
memory_search
memory_read_entry
```

Include:

- schema SQLite;
- FTS con tabella esterna `entry_id`;
- project resolver;
- `allowedRoots`;
- audit base;
- smoke test.

Per ridurre il rischio di PR troppo ampia, M1 puo' essere spezzata in tre sotto-step:

```text
M1a - Core store
  schema SQLite
  project resolver
  memory_status
  memory_add_entry
  memory_search
  memory_read_entry
  test unitari minimi

M1b - Safety and audit
  allowedRoots completo
  redazione audit
  isolamento cross-project
  error shape stabile
  smoke stdio dedicato

M1c - Search quality
  ranking FTS prevedibile
  snippet
  warning/verification_hint
  fallback LIKE se FTS non disponibile
```

Dettaglio stato M1b/M1c implementato:

- M1b:
  - `allowedRoots` risolto con precedence progetto/launcher e enforcement su `project_path`/`source_path`.
  - audit log con redazione campi sensibili e metadati redatti (hash/size) in persistenza.
  - guard cross-project in modalita' project e shape errori stabile (`INVALID_PARAM`, `NOT_FOUND`).
  - smoke stdio dedicato con verifica handshake/tool flow/error shape/audit.
- M1c:
  - ricerca FTS con ordinamento prevedibile (`bm25`, tie-break su `updated_at`).
  - snippet FTS e `verification_hint` stabile nei risultati.
  - fallback LIKE token-based in stile `docs-node` (escape + termini in AND) sia per assenza FTS sia per errori MATCH malformed.
  - warning esplicito in `memory_status`/`memory_search` in modalita' degraded (FTS non disponibile).

Ogni sotto-step deve restare compatibile col contratto M1; non introdurre import/export, resources, prompts o vettoriale prima della chiusura di M1.

### Milestone 2 - Gestione progetto e tassonomia

Tool:

```text
memory_list_scopes
memory_list_topics
memory_list_tags
memory_invalidate_entry
```

Include:

- status active/invalidated;
- tags;
- ranking migliorato.

Stato: completata in scope minimo.

- `memory_list_scopes`, `memory_list_topics`, `memory_list_tags` espongono tassonomia derivata dalle entry, filtrata per progetto e `status` (default `active`).
- `memory_invalidate_entry` imposta `status='invalidated'`, `invalidated_at` e `invalidation_reason`, con isolamento progetto e idempotenza.
- Ranking search M2: FTS usa `bm25` come criterio primario e tie-break su `importance`, `confidence`, `updated_at`; fallback LIKE usa gli stessi tie-break dopo `like_score`.
- `memory_status` e i tool di listing sono read-only effettivi: non materializzano righe in `projects`; la registrazione progetto avviene nei flussi di write come `memory_add_entry`.
- Copertura: test integration per tassonomia/invalidation/ranking e smoke stdio aggiornato con tools/list, annotations e flow M2.

### Milestone 3 - Import/export

Tool:

```text
memory_export_project
memory_import_project
```

Include:

- JSON;
- ZIP opzionale;
- conflict policy;
- documentazione setup.

### Milestone 4 - Resources/prompts MCP

Aggiungere:

```text
resources/list
resources/read
prompts/list
prompts/get
```

Solo se non complica compatibilita' host.

### Milestone 5 - Link e handoff

Tool:

```text
memory_link_entries
memory_list_entry_links
memory_handoff_summary
```

### Milestone 6 - Backend semantico opzionale

Aggiungere:

```text
MEMORY_ENABLE_VECTOR=1
MEMORY_VECTOR_BACKEND=sqlite-vec|...
```

## 26. Rischi e mitigazioni

| Rischio | Impatto | Mitigazione |
|---|---:|---|
| Memoria usata come fonte autoritativa | Alto | Mostrare source/status/confidence; docs/code restano fonti primarie |
| Leak cross-project | Alto | `mode=project` default; cross-project esplicito |
| Salvataggio segreti | Alto | redazione audit, warning, no auto-mining v1 |
| Crescita DB | Medio | limiti, archive, export, vacuum futuro |
| Host MCP severi rifiutano schema | Medio | schema semplice, array/items, smoke |
| Dipendenze native SQLite | Medio | usare dipendenze gia' compatibili con workspace |
| Duplicazione con docs-node | Medio | confini espliciti docs vs memory |
| Troppi tool | Medio | v1 con tool minimi |
| Obsolescenza memoria | Medio | status, invalidation, supersedes |
| Integrazione setup complessa | Basso/Medio | seguire pattern setup esistenti |

## 27. Criteri di accettazione v1

Una v1 e' accettabile quando:

1. `memory-node` compila con `npm run build`;
2. parte via stdio;
3. risponde a `tools/list`;
4. crea DB SQLite in path configurato;
5. risolve `project_path` e `project_id`;
6. rifiuta `source_path` fuori dalle root consentite risolte da `project_path`/`MEMORY_ALLOWED_ROOTS`;
7. aggiunge entry con `memory_add_entry`;
8. trova entry con `memory_search`;
9. legge entry con `memory_read_entry`;
10. registra audit log redatto;
11. smoke test dedicato passa;
12. `tests/smoke/run-all.mjs` include il nuovo smoke;
13. README e capability matrix aggiornati;
14. documentazione indica che serve riavvio server MCP se cambia codice runtime;
15. isolamento cross-project verificato in test (default project-mode);
16. redazione audit verificata su campi sensibili.
17. `memory_search` e `memory_read_entry` espongono `verification_hint` e metadati fonte/status/confidence.
18. gli errori principali usano `structuredContent.ok=false`, `error_code` e `detail`.

## 28. Decisioni aperte

1. Nome definitivo:
   - `memory-node`
   - `project-memory-node`
   - `ops-memory-node`

   Raccomandazione: `memory-node`, semplice e coerente.

2. DB default:
   - package-local;
   - user home;
   - path obbligatorio.

   Raccomandazione: user home, con override `MEMORY_DB_PATH`.

3. Cross-project search:
   - disabilitata;
   - abilitata con flag.

   Raccomandazione: disabilitata di default, abilitata solo con `mode="cross_project"`.

4. Hard delete:
   - permesso;
   - solo invalidate.

   Raccomandazione: supportare delete con `confirm=true`, ma preferire invalidate.

5. Resources MCP in v1:
   - si;
   - no.

   Raccomandazione: no nella prima PR comportamentale, si in milestone successiva.

6. Vettoriale:
   - subito;
   - dopo FTS stabile.

   Raccomandazione: dopo FTS stabile.

## 29. Bozza README `memory-node`

```markdown
# memory-node

Server MCP TypeScript/Node.js per memoria operativa locale multi-progetto.

Il server e':
- local-first;
- cross-project;
- guidato da `project_path`;
- basato su SQLite/FTS5;
- compatibile con workflow MCP/skills/agents aziendali;
- pensato per salvare decisioni, handoff, note operative e contesto riusabile.

## Setup

```bash
cd memory-node
npm install
npm run build
```

## Avvio

```bash
node dist/index.js
```

Nel launcher generato `MEMORY_ALLOWED_ROOTS` resta vuoto, cosi' la root operativa primaria viene risolta dal `project_path`.

## Tool principali

Core v1 minima:

- `memory_status`
- `memory_add_entry`
- `memory_search`
- `memory_read_entry`

Milestone successive:

- `memory_list_scopes`
- `memory_list_topics`
- `memory_list_tags`
- `memory_invalidate_entry`
- `memory_export_project`
- `memory_import_project`

## Regola importante

Passare sempre `project_path` quando la memoria riguarda un repository applicativo.
Il repo `mcp-servers` e' il toolbelt, non il progetto target.
```

## 30. Conclusione

`memory-node` e' tecnicamente giustificato, ma deve nascere con confini stretti.

La scelta piu' solida e' una v1 locale, SQLite/FTS5, cross-project, guidata da `project_path`, con audit log e senza vettoriale obbligatorio. Questo permette di ottenere subito continuita' operativa per agenti e skill senza introdurre complessita' runtime e senza confondere memoria operativa con documentazione ufficiale.

La regola architetturale da preservare e':

```text
mcp-servers distribuisce strumenti.
I progetti aziendali target forniscono il contesto.
memory-node collega i due tramite project_path, project_id e policy sicure.
```

## 31. Test reali MCP ed esito (2026-05-11)

Questa sezione registra una validazione end-to-end eseguita via MCP come uso reale operativo, con `project_path` esplicito e senza repo Git dedicato per il workspace esterno simulato.

### 31.1 Obiettivo

Verificare che il contratto v1 sia rispettato su chiamate reali:

- `memory_status`
- `memory_search`
- `memory_add_entry`
- `memory_read_entry`

con focus su:

- policy `allowedRoots`;
- dedupe su inserimenti duplicati;
- isolamento progetto (`mode=project`) e visibilita' cross-project (`mode=cross_project`);
- presenza metadati minimi (`source_type`, `source_ref`, `status`, `confidence`, `verification_hint`).

### 31.2 Contesto test

- Workspace principale: `e:\mcp-servers`
- Workspace esterno simulato (dentro root consentita): `e:\mcp-servers\tmp\external-repo-memory-e2e`
- Tentativo path esterno fuori root: `e:\memory-e2e-external` (atteso rifiuto policy)

### 31.3 Esecuzione sintetica

1. Verifica operativita' MCP memoria:
  - `memory_search` su token test nel progetto principale: nessun risultato iniziale.
2. Verifica `allowedRoots`:
  - `memory_status` su `e:\memory-e2e-external`: errore `INVALID_PARAM` coerente con policy.
3. Test add/search/read sul workspace esterno simulato:
  - `memory_add_entry` con `dedupe=true` e metadati completi.
  - `memory_search` con `include_content=true` ritorna entry e snippet evidenziato.
  - `memory_read_entry` ritorna contenuto completo e metadati.
4. Test dedupe:
  - seconda `memory_add_entry` identica con `dedupe=true` ritorna `deduped=true` e `existing_entry_id`.
5. Test isolamento hard con `project_id` esplicito:
  - add con `project_id="workspace:external-memory-e2e"`.
  - ricerca `mode=project` nel progetto principale: nessun risultato per quel token.
6. Test visibilita' cross-project:
  - `memory_search` con `mode="cross_project"` trova entry di entrambi i namespace progetto.

### 31.4 Evidenze principali

- Entry creata su namespace risolto da Git repo padre:
  - `entry_id`: `ad13c119-e2ba-492e-9bd6-56fa27a299bc`
  - `project_id`: `git:github.com/sophiadeveloper/mcp-servers`
- Entry creata con namespace forzato:
  - `entry_id`: `2d33db14-ccc1-4e66-b23f-a4e3a114eb09`
  - `project_id`: `workspace:external-memory-e2e`

Comportamento osservato e rilevante:

- cartella esterna annidata nel repo principale, senza `project_id` esplicito, risolve lo stesso `project_id` Git del repo padre;
- per isolamento forte in scenario annidato, usare `project_id` esplicito.

### 31.5 Esito rispetto a specifiche v1

Esito complessivo: conforme alle specifiche v1.

Dettaglio conformita':

1. Tool M1 supportati e funzionanti (`memory_status`, `memory_add_entry`, `memory_search`, `memory_read_entry`).
2. Path policy M1b rispettata: rifiuto path fuori `allowedRoots`.
3. Dedupe M1a rispettato con `dedupe=true`.
4. Search quality M1c rispettata: snippet `>>>...<<<` e `verification_hint` stabile.
5. Isolamento project-mode coerente con risoluzione `project_id` (priorita' input esplicito > env progetto > git > fallback hash path).

Nota operativa:

- In test "workspace esterno" annidato nel repo principale, il comportamento e' coerente ma puo' sembrare controintuitivo; se serve separazione certa usare `project_id` esplicito.

### 31.6 Follow-up consigliato

- Aggiungere uno smoke/integration test dedicato che copra esplicitamente il caso di path annidato e confronto tra:
  - risoluzione automatica `project_id`;
  - risoluzione con override `project_id`.

Questo rende il comportamento atteso immediatamente verificabile e previene regressioni su isolamento progetto.
