# Analisi tecnica implementativa — Mappa mentale documentale per `docs-node` e correlazioni operative in `memory-node`

Data: 2026-06-03  
Repository target: `sophiadeveloper/mcp-servers`  
Branch target: `master`  
Ambito principale: `docs-node`  
Ambito secondario: `memory-node`  

## 1. Sintesi implementativa

L’evolutiva introduce una “mappa mentale” tra documenti indicizzati, con link direzionali uno-a-molti/m molti-a-molti, correlazione automatica via euristiche e supporto a relazioni parent tra shelf.

Il caso guida è:

```text
Shelf parent: Framework Standard
Shelf child: Cliente X
```

I documenti dello shelf `Cliente X` possono correlarsi di default ai documenti dello shelf `Framework Standard`, perché il parent è fonte generale di riferimento. Il verso opposto non è automatico: i documenti del framework non devono esporre link verso i documenti cliente, salvo link esplicito creato in direzione `Framework -> Cliente`.

La soluzione proposta è quindi:

```text
docs-node
  - shelf_links: definisce relazioni parent/child tra shelf
  - document_links: definisce link direzionali tra documenti
  - correlazione euristica: genera link source_document -> target_document
  - JSON arricchiti: documenti, search result e read_document espongono link outgoing/parent context

memory-node
  - memoria operativa resta separata
  - link opzionali tra entry e documenti tramite URI, non tramite DB condiviso
```

La priorità implementativa è su `docs-node`. `memory-node` può essere trattato come fase successiva, usando URI `docs://document/{id}` per collegare note operative ai documenti.

## 2. Stato attuale rilevante

### 2.1 `docs-node`

`docs-node/index.js` contiene già:

- store SQLite locale `docs.db`;
- tabelle `shelves`, `documents`, `tags`, `document_tags`, `scan_sources`, `feature_state`;
- indicizzazione FTS5 su `documents_fts` con fallback degradato;
- tool MCP `docs_management`;
- tool MCP `docs_navigation`;
- risorse MCP `docs://shelf/...` e `docs://document/...`;
- flussi `scan_file`, `scan_folder`, `resync_all`, `export_shelf`, `import_shelf`.

Il punto di aggancio naturale per la nuova feature è dopo `upsertDocument()`/`syncMarkdownFile()` e nelle funzioni di lettura/lista/ricerca.

### 2.2 `memory-node`

`memory-node` ha già:

- `memory_entries` project-scoped;
- `source_type`, `source_ref`, `source_path`, `source_uri`;
- tags;
- FTS/fallback LIKE;
- `mode="cross_project"` esplicito;
- audit log opzionale.

La documentazione tecnica già prevede concettualmente `memory_links`, ma il runtime letto non crea ancora la tabella. Questa parte è utile ma non bloccante per l’evolutiva `docs-node`.

## 3. Requisiti funzionali consolidati

### 3.1 Link documentali

I documenti devono poter essere collegati uno-a-molti. Tecnicamente il modello corretto è molti-a-molti direzionale:

```text
document A -> document B
document A -> document C
document D -> document A
```

Il link inverso non è implicito.

### 3.2 Correlazione durante creazione/update documento

Nel codice attuale “creazione/update documento” corrisponde a:

- `docs_management scan_file`;
- `docs_management scan_folder`;
- `docs_management resync_all`;
- `import_shelf`, in forma indiretta.

La correlazione automatica deve essere attivabile durante questi flussi tramite flag espliciti, ma non deve diventare default distruttivo o rallentare i sync esistenti.

### 3.3 Correlazione massiva su shelf

Serve una funzione dedicata che scandisca completamente uno shelf e generi link con algoritmo euristico.

Il batch deve supportare:

- dry-run;
- apply esplicito;
- soglia di confidenza;
- limite link per documento;
- report dei link candidati/applicati;
- sostituzione controllata dei soli link euristici.

### 3.4 Shelf parent/child

Uno shelf può dichiarare uno o più parent shelf.

Regola:

```text
child -> parent permesso di default
parent -> child non permesso di default
```

Questo permette a uno shelf cliente di correlare i propri documenti alla documentazione standard del framework senza “sporcare” il framework con backlink client-specific.

### 3.5 Esposizione link nei JSON

I link ai documenti e agli shelf correlati devono essere esposti direttamente nel JSON restituito dalle API di consultazione, in modo che l’agente veda subito la mappa e possa consultare i correlati senza chiamate esplorative aggiuntive.

In particolare:

- `list_documents` deve poter includere un sommario link;
- `search` deve poter includere link rilevanti nei risultati;
- `read_document` deve poter includere link completi;
- i JSON devono includere URI MCP già consultabili: `docs://document/{id}` e `docs://shelf/{id}-{slug}`.

## 4. Modello dati proposto

### 4.1 Relazioni tra shelf

Nuova tabella:

```sql
CREATE TABLE IF NOT EXISTS shelf_links (
  parent_shelf_id INTEGER NOT NULL REFERENCES shelves(id) ON DELETE CASCADE,
  child_shelf_id  INTEGER NOT NULL REFERENCES shelves(id) ON DELETE CASCADE,
  relation        TEXT NOT NULL DEFAULT 'parent',
  created_at      TEXT DEFAULT (datetime('now')),
  updated_at      TEXT DEFAULT (datetime('now')),
  PRIMARY KEY(parent_shelf_id, child_shelf_id, relation),
  CHECK(parent_shelf_id <> child_shelf_id)
);

CREATE INDEX IF NOT EXISTS idx_shelf_links_child
ON shelf_links(child_shelf_id);

CREATE INDEX IF NOT EXISTS idx_shelf_links_parent
ON shelf_links(parent_shelf_id);
```

Semantica:

```text
parent_shelf_id = fonte generale / framework / base comune
child_shelf_id  = fonte specifica / cliente / progetto
```

Regole applicative:

- uno shelf può avere più parent;
- non sono ammessi self-link;
- va impedita la creazione di cicli;
- la relazione iniziale supportata è `parent`, ma il campo `relation` lascia spazio a future varianti.

### 4.2 Link direzionali tra documenti

Nuova tabella:

```sql
CREATE TABLE IF NOT EXISTS document_links (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  source_document_id INTEGER NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
  target_document_id INTEGER NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
  relation TEXT NOT NULL,
  score REAL,
  confidence REAL,
  origin TEXT NOT NULL,
  heuristic_version TEXT,
  label TEXT,
  rationale TEXT,
  metadata_json TEXT,
  created_at TEXT DEFAULT (datetime('now')),
  updated_at TEXT DEFAULT (datetime('now')),
  UNIQUE(source_document_id, target_document_id, relation, origin),
  CHECK(source_document_id <> target_document_id)
);

CREATE INDEX IF NOT EXISTS idx_document_links_source
ON document_links(source_document_id);

CREATE INDEX IF NOT EXISTS idx_document_links_target
ON document_links(target_document_id);

CREATE INDEX IF NOT EXISTS idx_document_links_relation
ON document_links(relation);

CREATE INDEX IF NOT EXISTS idx_document_links_origin
ON document_links(origin);
```

Il campo `direction` non serve: la direzione è rappresentata da `source_document_id -> target_document_id`.

Relazioni iniziali consigliate:

```text
related_to
same_topic
references_standard
extends_standard
depends_on
derived_from
supersedes
duplicates
mentions
```

Origini:

```text
manual      link creato esplicitamente dall'utente/agente
mcp         link creato da tool MCP in un workflow
heuristic   link generato dall'algoritmo
import      link importato da manifest
```

## 5. Patch DB e rilascio

`docs-node` ha già un pattern di upgrade idempotente nel bootstrap DB:

- `CREATE TABLE IF NOT EXISTS`;
- `CREATE INDEX IF NOT EXISTS`;
- `ALTER TABLE` protetto da controllo colonna;
- `feature_state` per inizializzazione feature.

La nuova evolutiva deve seguire lo stesso approccio. Non va richiesta una migrazione manuale.

### 5.1 Modifiche in `initDb()`

Aggiungere a `initDb()`:

```js
await runQuery(`
  CREATE TABLE IF NOT EXISTS shelf_links (
    parent_shelf_id INTEGER NOT NULL REFERENCES shelves(id) ON DELETE CASCADE,
    child_shelf_id  INTEGER NOT NULL REFERENCES shelves(id) ON DELETE CASCADE,
    relation        TEXT NOT NULL DEFAULT 'parent',
    created_at      TEXT DEFAULT (datetime('now')),
    updated_at      TEXT DEFAULT (datetime('now')),
    PRIMARY KEY(parent_shelf_id, child_shelf_id, relation),
    CHECK(parent_shelf_id <> child_shelf_id)
  )
`);

await runQuery(`
  CREATE TABLE IF NOT EXISTS document_links (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    source_document_id INTEGER NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
    target_document_id INTEGER NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
    relation TEXT NOT NULL,
    score REAL,
    confidence REAL,
    origin TEXT NOT NULL,
    heuristic_version TEXT,
    label TEXT,
    rationale TEXT,
    metadata_json TEXT,
    created_at TEXT DEFAULT (datetime('now')),
    updated_at TEXT DEFAULT (datetime('now')),
    UNIQUE(source_document_id, target_document_id, relation, origin),
    CHECK(source_document_id <> target_document_id)
  )
`);
```

Poi aggiungere gli indici.

### 5.2 Feature state

Estendere `KNOWN_FEATURES` con:

```js
shelf_links: {
  noticeText: [
    "Notice: shelf parent links are available.",
    "Use docs_management set_shelf_parents to define framework/base shelves for client-specific shelves."
  ].join("\n")
},
document_links: {
  noticeText: [
    "Notice: document links are available.",
    "Use docs_management correlate_shelf or correlate_document to generate directional related-document links."
  ].join("\n")
}
```

In alternativa, evitare notice rumorose e usare solo `feature_status`.

### 5.3 Compatibilità installazioni esistenti

L’upgrade è sicuro perché:

```text
database nuovo       -> crea tabelle e indici
database esistente   -> aggiunge tabelle e indici mancanti
riavvio successivo   -> nessuna modifica distruttiva
```

Non devono essere cancellati o ricalcolati link manuali durante batch euristici.

## 6. Contratto MCP condensato

Per evitare una superficie troppo frammentata, conviene condensare le azioni in tre gruppi logici.

### 6.1 Gestione shelf relationship

Invece di molte azioni granulari (`add_shelf_parent`, `remove_shelf_parent`, ecc.), usare una singola azione principale:

```text
docs_management manage_shelf_links
```

Esempi input:

```json
{
  "action": "manage_shelf_links",
  "operation": "set_parents",
  "shelf": "Cliente X",
  "parent_shelves": ["Framework Standard", "Linee guida architetturali"]
}
```

```json
{
  "action": "manage_shelf_links",
  "operation": "add_parent",
  "shelf": "Cliente X",
  "parent_shelf": "Framework Standard"
}
```

```json
{
  "action": "manage_shelf_links",
  "operation": "list",
  "shelf": "Cliente X",
  "include_children": true
}
```

Operazioni supportate:

```text
set_parents
add_parent
remove_parent
list
```

Motivazione: meno azioni nello schema MCP, evoluzione più semplice.

### 6.2 Gestione link documento

Usare una singola azione:

```text
docs_management manage_document_links
```

Esempi:

```json
{
  "action": "manage_document_links",
  "operation": "add",
  "source_document_id": 101,
  "target_document_id": 22,
  "relation": "references_standard",
  "confidence": 0.9,
  "label": "Riferimento framework standard",
  "rationale": "Il documento client implementa una variante del flusso standard descritto nel documento framework."
}
```

```json
{
  "action": "manage_document_links",
  "operation": "list",
  "document_id": 101,
  "direction": "outgoing"
}
```

```json
{
  "action": "manage_document_links",
  "operation": "remove",
  "link_id": 55
}
```

Operazioni:

```text
add
remove
list
replace_for_document
```

Default importante:

```text
direction = outgoing
```

Solo se richiesto:

```text
direction = incoming | both
```

### 6.3 Correlazione euristica

Una sola azione:

```text
docs_management correlate
```

Parametri principali:

```json
{
  "action": "correlate",
  "mode": "document",
  "document_id": 101,
  "scope": "shelf_with_parents",
  "include_parent_depth": 1,
  "min_confidence": 0.6,
  "max_links_per_document": 8,
  "apply": false
}
```

```json
{
  "action": "correlate",
  "mode": "shelf",
  "shelf": "Cliente X",
  "scope": "shelf_with_parents",
  "include_parent_depth": 1,
  "min_confidence": 0.6,
  "max_links_per_document": 8,
  "replace_heuristic_links": true,
  "apply": true
}
```

Mode:

```text
document
shelf
```

Scope:

```text
shelf
shelf_with_parents
cross_shelf
```

Default:

```text
scope = shelf_with_parents
include_parent_depth = 1
apply = false
```

`cross_shelf` deve essere esplicito e preferibilmente richiedere `target_shelves` o `allow_all_shelves=true`.

## 7. JSON arricchiti in document info e search result

### 7.1 Obiettivo

L’agente deve vedere subito che un documento ha correlazioni utili e deve poterle seguire con URI MCP senza chiamare un tool dedicato, salvo necessità di dettaglio.

### 7.2 Nuovo parametro comune

Aggiungere ai tool di navigazione:

```json
{
  "include_links": true,
  "links_direction": "outgoing",
  "links_limit": 5,
  "include_parent_context": true
}
```

Default consigliati:

```text
include_links = false per compatibilità iniziale
links_direction = outgoing
links_limit = 5
include_parent_context = true nei risultati arricchiti
```

Valutazione: rendere `include_links=true` di default nei risultati search può essere utile ma rischia di aumentare payload e costo query. Compromesso consigliato:

- `read_document`: `include_links=true` di default;
- `search`: `include_links=false` come default iniziale, ma facilmente attivabile;
- `list_documents`: `include_links=false`, con `link_summary=true` per conteggi leggeri.

Se si vuole massima evidenza operativa, si può impostare `include_links=true` di default per `search` con `links_limit=3`. È accettabile se i test confermano payload stabile.

### 7.3 Shape per `read_document`

Quando viene letto un documento:

```json
{
  "id": 101,
  "title": "Personalizzazione Cliente X - Export ordini",
  "file_path": "/docs/client-x/export-ordini.md",
  "shelf": {
    "id": 7,
    "name": "Cliente X",
    "uri": "docs://shelf/7-cliente-x",
    "parents": [
      {
        "id": 2,
        "name": "Framework Standard",
        "uri": "docs://shelf/2-framework-standard",
        "relation": "parent"
      }
    ]
  },
  "resource_uri": "docs://document/101",
  "links": {
    "outgoing": [
      {
        "link_id": 55,
        "relation": "references_standard",
        "label": "Flusso standard export ordini",
        "confidence": 0.86,
        "origin": "heuristic",
        "target": {
          "id": 22,
          "title": "Framework Standard - Export ordini",
          "shelf_id": 2,
          "shelf_name": "Framework Standard",
          "resource_uri": "docs://document/22",
          "shelf_uri": "docs://shelf/2-framework-standard"
        },
        "rationale": "Titolo simile, heading condivisi e parent shelf Framework Standard."
      }
    ],
    "incoming_count": 0
  }
}
```

Nota: `incoming_count` può essere esposto senza dettaglio, così il parent sa eventualmente che esistono riferimenti inbound ma non li “espone” come correlati navigabili di default. Per rispettare rigidamente il requisito, anche `incoming_count` può essere omesso di default.

### 7.4 Shape per `search`

Risultato search arricchito:

```json
{
  "id": 101,
  "title": "Personalizzazione Cliente X - Export ordini",
  "file_path": "/docs/client-x/export-ordini.md",
  "shelf_id": 7,
  "shelf_name": "Cliente X",
  "resource_uri": "docs://document/101",
  "shelf_uri": "docs://shelf/7-cliente-x",
  "snippet": "...export ordini...",
  "links": {
    "outgoing": [
      {
        "relation": "references_standard",
        "confidence": 0.86,
        "target_title": "Framework Standard - Export ordini",
        "target_resource_uri": "docs://document/22",
        "target_shelf_name": "Framework Standard",
        "target_shelf_uri": "docs://shelf/2-framework-standard"
      }
    ],
    "outgoing_count": 4
  },
  "shelf_context": {
    "parents": [
      {
        "name": "Framework Standard",
        "uri": "docs://shelf/2-framework-standard"
      }
    ]
  }
}
```

Per contenere payload:

- nei risultati search includere link sintetici;
- in `read_document` includere link completi con `rationale`;
- usare `links_limit`.

### 7.5 Shape per `list_documents`

Per `list_documents`, evitare link completi su liste grandi. Meglio un sommario:

```json
{
  "id": 101,
  "title": "Personalizzazione Cliente X - Export ordini",
  "shelf_id": 7,
  "shelf_name": "Cliente X",
  "resource_uri": "docs://document/101",
  "shelf_uri": "docs://shelf/7-cliente-x",
  "link_summary": {
    "outgoing_count": 4,
    "incoming_count": 0,
    "top_relations": [
      { "relation": "references_standard", "count": 2 },
      { "relation": "same_topic", "count": 2 }
    ]
  },
  "shelf_context": {
    "parent_count": 1
  }
}
```

Parametro:

```json
{
  "action": "list_documents",
  "shelf": "Cliente X",
  "link_summary": true,
  "include_parent_context": true
}
```

## 8. Query helper da introdurre

### 8.1 Parent shelf

```js
async function getShelfParents(shelfId) {
  return allQuery(`
    SELECT p.id, p.name, p.description
    FROM shelf_links sl
    JOIN shelves p ON p.id = sl.parent_shelf_id
    WHERE sl.child_shelf_id = ?
      AND sl.relation = 'parent'
    ORDER BY p.name
  `, [shelfId]);
}
```

### 8.2 Candidate shelf per correlazione

```js
async function getCandidateShelfIdsForDocument(sourceShelfId, {
  scope = "shelf_with_parents",
  includeParentDepth = 1,
  targetShelves = []
} = {}) {
  if (scope === "shelf") return [sourceShelfId];

  if (scope === "shelf_with_parents") {
    const parentIds = await getParentShelfIds(sourceShelfId, includeParentDepth);
    return [sourceShelfId, ...parentIds];
  }

  if (scope === "cross_shelf") {
    return resolveExplicitTargetShelfIds(targetShelves);
  }

  throw new Error(`Scope correlazione non valido: ${scope}`);
}
```

### 8.3 Link outgoing sintetici

```js
async function attachOutgoingLinks(rows, { limit = 5 } = {}) {
  const documentIds = rows.map((row) => row.id);
  if (documentIds.length === 0) return rows;

  const placeholders = documentIds.map(() => "?").join(",");
  const links = await allQuery(`
    SELECT
      dl.id AS link_id,
      dl.source_document_id,
      dl.relation,
      dl.confidence,
      dl.origin,
      dl.label,
      target.id AS target_id,
      target.title AS target_title,
      target.file_path AS target_file_path,
      target_shelf.id AS target_shelf_id,
      target_shelf.name AS target_shelf_name
    FROM document_links dl
    JOIN documents target ON target.id = dl.target_document_id
    JOIN shelves target_shelf ON target_shelf.id = target.shelf_id
    WHERE dl.source_document_id IN (${placeholders})
    ORDER BY dl.confidence DESC, dl.updated_at DESC
  `, documentIds);

  // raggruppare per source_document_id e troncare a limit
}
```

## 9. Algoritmo euristico implementativo

### 9.1 Candidate generation

Non confrontare ogni documento contro tutto il corpus quando non serve. Per ogni documento sorgente:

1. calcolare shelf candidati;
2. estrarre token da titolo;
3. estrarre heading Markdown;
4. recuperare tag;
5. usare FTS per produrre top N candidati;
6. unire candidati da:
   - stesso shelf;
   - parent shelf;
   - tag overlap;
   - path proximity;
   - riferimenti espliciti.

### 9.2 Scoring

Formula iniziale:

```text
score =
  0.25 title_similarity
+ 0.20 heading_similarity
+ 0.20 tag_overlap
+ 0.15 fts_reciprocal_match
+ 0.10 path_proximity
+ 0.10 explicit_reference_match
```

La confidenza può essere derivata da score normalizzato e penalizzata se il link attraversa shelf non parent.

Per `shelf_with_parents`, un link child→parent può ricevere bonus controllato:

```text
parent_reference_bonus = 0.05
```

solo se il target shelf è parent diretto.

### 9.3 Relazione suggerita

Regole semplici:

```text
target in parent shelf + alta similarità -> references_standard
target in parent shelf + source contiene "estende/custom" -> extends_standard
stesso shelf + alta similarità titolo/tag -> same_topic
contenuto contiene path/nome file target -> mentions
quasi duplicato -> duplicates
fallback -> related_to
```

### 9.4 Report batch

Output `correlate` in modalità shelf:

```json
{
  "ok": true,
  "action": "correlate",
  "mode": "shelf",
  "shelf": "Cliente X",
  "scope": "shelf_with_parents",
  "apply": true,
  "heuristic_version": "docs-links-v1",
  "scanned_documents": 120,
  "candidate_links": 430,
  "applied_links": 210,
  "skipped_low_confidence": 180,
  "skipped_existing": 40,
  "warnings": [],
  "sample_links": [
    {
      "source_document_id": 101,
      "target_document_id": 22,
      "relation": "references_standard",
      "confidence": 0.86,
      "target_shelf": "Framework Standard",
      "rationale": "Parent shelf + similarità heading + tag export."
    }
  ]
}
```

## 10. Export/import

L’export attuale usa `DOCS_EXPORT_FORMAT_VERSION = 1`. Per includere link serve versione 2.

### 10.1 Manifest v2

Aggiungere:

```json
{
  "shelf_links": [
    {
      "parent_shelf_name": "Framework Standard",
      "child_shelf_name": "Cliente X",
      "relation": "parent"
    }
  ],
  "document_links": [
    {
      "source_relative_path": "client/export.md",
      "target_relative_path": "../framework/export.md",
      "relation": "references_standard",
      "origin": "manual",
      "confidence": 0.9,
      "label": "Riferimento export standard",
      "rationale": "..."
    }
  ]
}
```

### 10.2 Compatibilità import

Regole:

```text
format_version 1 -> import senza link
format_version 2 -> import shelf_links e document_links
```

Se un link non è risolvibile:

```json
{
  "relative_path": "client/export.md",
  "warning": "document_link target non risolto: ../framework/export.md"
}
```

## 11. `memory-node`: integrazione consigliata

`memory-node` non deve condividere il DB di `docs-node`.

Per collegare memoria operativa a documenti:

```sql
CREATE TABLE IF NOT EXISTS memory_links (
  id TEXT PRIMARY KEY,
  project_id TEXT NOT NULL,
  source_entry_id TEXT,
  target_entry_id TEXT,
  target_uri TEXT,
  target_type TEXT NOT NULL,
  relation TEXT NOT NULL,
  label TEXT,
  confidence REAL,
  origin TEXT NOT NULL,
  rationale TEXT,
  metadata_json TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  FOREIGN KEY(source_entry_id) REFERENCES memory_entries(id) ON DELETE CASCADE,
  FOREIGN KEY(target_entry_id) REFERENCES memory_entries(id) ON DELETE CASCADE
);
```

Esempio:

```json
{
  "source_entry_id": "memory-entry-uuid",
  "target_type": "docs_document",
  "target_uri": "docs://document/101",
  "relation": "derived_from",
  "confidence": 0.8
}
```

Questa fase può seguire dopo la stabilizzazione di `docs-node`.

## 12. File principali impattati

### `docs-node/index.js`

Modifiche:

- `KNOWN_FEATURES`;
- `initDb`;
- helper shelf links;
- helper document links;
- helper correlation;
- schema `docs_management`;
- schema `docs_navigation`;
- `searchDocuments`;
- `listDocuments`;
- lettura documenti;
- export/import manifest.

### `tests/smoke/docs-node.smoke.mjs`

Aggiungere scenario end-to-end:

1. crea due shelf;
2. crea parent relation;
3. scansiona documento parent;
4. scansiona documento child;
5. esegue correlazione child;
6. verifica link child→parent;
7. verifica che parent non esponga outgoing verso child;
8. verifica incoming solo se richiesto;
9. verifica search result con `include_links=true`.

### Documentazione

Aggiornare:

- README `docs-node`;
- eventuale capability matrix;
- changelog.

## 13. Piano PR consigliato

### PR 1 — Schema e JSON link-ready

- Tabelle `shelf_links` e `document_links`;
- helper base;
- `feature_status`;
- nessuna euristica;
- test migrazione idempotente.

### PR 2 — Gestione manuale via MCP

- `manage_shelf_links`;
- `manage_document_links`;
- JSON `read_document` con `include_links`;
- test direzionalità.

### PR 3 — Search/list arricchiti

- `include_links`;
- `link_summary`;
- `shelf_context`;
- test payload search.

### PR 4 — Correlazione euristica documento/shelf

- `correlate`;
- dry-run/apply;
- parent scope default;
- report batch.

### PR 5 — Export/import v2

- manifest v2;
- import compatibile v1;
- warning link non risolti.

### PR 6 — `memory-node` links

- `memory_links`;
- tool link minimi;
- URI docs esterni.

## 14. Rischi

### Rumore nei risultati search

Se `include_links=true` di default su search, il payload può diventare più grande e rumoroso. Mitigazione:

```text
links_limit = 3
solo outgoing
solo campi sintetici
```

### Backlink parent/child

Rischio: esporre incoming link su documenti parent può far sembrare che il framework dipenda dal client. Mitigazione:

```text
read/search espongono solo outgoing di default
incoming solo se direction=incoming/both
```

### Cicli shelf

Shelf parent ciclici romperebbero la logica di candidate scope. Mitigazione: validare transitive closure prima di inserire un parent.

### Performance batch

Con molti documenti, il confronto completo N×M può essere costoso. Mitigazione: candidate generation via FTS/tag/path e limiti configurabili.

### Import/export

Manifest v2 deve restare compatibile con v1. Non incrementare versione senza fallback.

## 15. Decisioni consigliate

1. Implementare link direzionali, non simmetrici.
2. Implementare parent shelf con child→parent default.
3. Non esporre parent→child salvo link esplicito o `incoming`.
4. Condensare le azioni MCP in:
   - `manage_shelf_links`;
   - `manage_document_links`;
   - `correlate`.
5. Esporre link nei JSON di documento e search tramite `include_links`.
6. Usare `read_document include_links=true` come default operativo.
7. Usare `search include_links=true` solo se il payload resta accettabile; altrimenti default `false` e `links_limit=3`.
8. Gestire rilascio con patch DB idempotente in `initDb()`.
9. Tenere `memory-node` separato e collegare via URI.

## 16. Criteri di accettazione

La feature è accettabile quando:

- un DB esistente viene aggiornato al primo avvio senza interventi manuali;
- è possibile dichiarare `Framework Standard` parent di `Cliente X`;
- la correlazione di un documento `Cliente X` può generare link verso documenti `Framework Standard`;
- il documento `Framework Standard` non espone link outgoing verso `Cliente X` se non creati esplicitamente;
- `read_document` può restituire link outgoing con URI documento/shelf;
- `search` può restituire link sintetici se `include_links=true`;
- il batch su shelf produce dry-run e apply;
- i link manuali non vengono cancellati da ricalcoli euristici;
- export/import v1 continua a funzionare;
- export/import v2 preserva shelf links e document links.
