---
type: note
tags: [note, vps-ia, grist, pkm, migration, opencode, Infra]
univers: PERSO
nature: documentation
date: "[[01_JOURNAL/2026/2026-11-04]]"
title: "PKM — test Grist comme base de données (2026-11-04)"
description: "Test de migration vers un document Grist PKM : 8 tables relationnelles, SilverBullet reste source de vérité."
projects:
  - [[03_PROJETS/VPS-IA]]
---

# VPS-IA — Test Grist comme base PKM (2026-11-04)

Migration d'essai des données SilverBullet du mois d'octobre vers un document **Grist « PKM »**, en complément de SilverBullet (source de vérité).

## Objectif

Tester l'utilisation de **Grist** (base relationnelle) comme outil de gestion du PKM (univers, domaines, projets, événements, réunions, tâches, notes, journal), les données restant sourcées dans SilverBullet.

## Résultat

- Nouveau document **`PKM`** (urlId `pyCAf89kXPiR`, org Personal / workspace Home). Les 2 documents d'essai intermédiaires (`cRuRftafcWAVo1CBh6goL7`, `7i1T8LcnHXALdZmLstzRuB`) ont été **supprimés**.
- **8 tables** relationnelles, colonnes Ref lisibles (`visibleCol`), liens SilverBullet en URL complète cliquable (sans `.md`).

### Schéma final

| Table | Lignes | Colonnes |
|---|---|---|
| `UNIVERS` | 3 | code, nom, couleur |
| `DOMAINES` | 23 | code, nom, univers_id→UNIVERS |
| `PROJETS` | 92 | code, titre, univers_id, domaine_id, statut, lien_sb |
| `EVENEMENTS` | 56 | date, heure_debut, heure_fin, intitule, projet_id, etat, lien_sb |
| `REUNIONS` | 47 | evenement_id→EVENEMENTS, contenu_note, synthese_ia, synthese_md, lien_sb |
| `TACHES` | 70 | texte, fait, date, projet_id, etat, origine |
| `NOTES` | 18 | titre, nature, tags, univers_id, page_sb, contenu_md |
| `JOURNAL` | 15 | date, lien_sb |

**Données migrées** : 3 univers · 23 domaines · 92 projets · 15 pages journal · 56 événements · 47 réunions (dont 21 avec synthèse IA) · 70 tâches · 18 notes datées d'octobre.

### Choix de conception (validés)

- **REUNIONS = 1 ligne par événement** : date/heure/projet déportés sur l'événement lié (`evenement_id` → EVENEMENTS) ; pas de colonne `Type`. `synthese_ia` (Bool) indique qu'une synthèse existe, son texte est dans `synthese_md`.
- **Liens SB** : type Text + URL complète `https://sb.datagrandest.net/shizendo/...` sans `.md` (pas de type URL natif dans Grist).
- **`contenu_md`/`contenu_note`** : markdown sans le frontmatter YAML (les attributs du frontmatter sont des colonnes).
- **Totaux** : tags "a b c", intitulés d'événements nettoyés (pas de `- ` initial, `#event`, ni chemin projet → repris en `projet_id`).

## Pièges rencontrés / leçons

1. **Corruption d'affichage Grist** : `widgetOptions` des colonnes Choice doit être un **JSON en chaîne** `{"choices":[...]}`. Avec `choices:[...]`, Grist renvoie `Unexpected token 'c'... is not valid JSON` et le document ne s'ouvre plus. **Fix** : `PATCH /tables/{t}/columns` avec `fields.widgetOptions` = chaîne JSON. → consigné dans le skill Grist.
2. **`list_mcp_resources` ≠ disponibilité des outils** : sur le serveur grist, cette liste est vide (pas de *resources*, seulement des outils). J'ai conclu à tort que le MCP était indisponible. Vérifier par un **appel de tool réel**, pas par `list_mcp_resources`. → consigné dans le skill Grist.
3. **Endpoints REST Grist** : lignes = `/tables/{t}/records` (pas `/rows`, ici 404) ; suppression = `/tables/{t}/data/delete` (body `[ids]`) ; `require` ne déduplique pas sur `/records`.
4. **Double-encodage URL SilverBullet** : le `%2E` du `.md` ne doit pas être re-encodé en `%252E` (sinon 404 → pas de header). Quoter le nom sans extension puis ajouter `%2Emd`.
5. **Extraction des tâches/événements** : les pages du jour sont des requêtes dynamiques (contenu nul) ; le réel vit dans les pages sources (Weeks, meetings, projets). Parser tout l'espace, dédoublonner par (date, heure, texte).
6. **Notes « créées en octobre »** : `x-created` reflète la migration (30/10), pas la création réelle ; git = 1 commit de masse. Retenu : nom ou frontmatter daté oct (18 notes). Périmètre arbitraire — à ajuster si besoin.

## Fichiers de référence

- Script migration finale : `/var/lib/opencode/.local/share/opencode/shell/migrate_v3.py` (lisible localement ; copie `/tmp/opencode/migrate_v3.py`).
- Vidage doc : `wipe3.py`.
- Skill mis à jour : [[80_CONFIG/IA_SKILLS/Grist]] (section PKM + pièges MCP/REST).

## À faire / reste en suspens

- Le périmètre `NOTES` (18) est volontairement strict ; étendre si besoin.
- Colonne `Table1` vide non supprimable (limite API de cette instance) — inoffensive.
- Documenter plus tard un **flux de sync SB → Grist automatisé** (script + cron) si le test est concluant.

## Voir aussi

- Skill [[80_CONFIG/IA_SKILLS/Grist]]
- Conventions SB : [[80_CONFIG/IA_SKILLS/Silverbullet-Conventions]]
- Mémoire : [[80_CONFIG/IA_MEMOIRE_LONGTERME]]