---
name: grist
description: "Accès et conventions de la base Grist (org Personal / Home) : documents MASTER, BUDGET, PKM, tables, bonnes pratiques via le MCP grist. Outil partagé par plusieurs agents (pkm-manager, vps-admin)."
---

# Skill: Grist — bases relationnelles

Grist est la base de données relationnelle de l'écosystème. Accès via le **MCP `grist`** (configuré dans `opencode.json`, clé `GRIST_API_KEY` depuis le coffre pass `opencode/grist-api-key`).

## ⚠️ Piège MCP vérifié (2026-11-04)

- `list_mcp_resources` liste les **resources** d'un serveur — Grist n'en expose **aucune** (il n'a que des outils). Une liste vide **ne prouve rien** sur la disponibilité des outils.
- Vérifier la disponibilité du MCP par un **appel de tool réel** (ex. `list_documents` / `list_tables`), pas par `list_mcp_resources`.
- Le MCP grist expose **54 outils** (`list_records`, `add_grist_records`, `create_table`, `list_tables`, `add_records`…). Les opérations de lignes passent par `/tables/{id}/records` (endpoint `/rows` = 404 sur cette instance).

## API REST (fallback, même couche que le MCP)

Le MCP ne fait que wraper l'API REST `https://grist.datagrandest.net/api` (clé `GRIST_API_KEY`). Usage direct possible :

```bash
source ~/.opencode/bin/opencode-secrets-env.sh   # expose GRIST_API_KEY
D="https://grist.datagrandest.net/api/docs/<doc_id>"
curl -H "Authorization: Bearer $GRIST_API_KEY" "$D/tables"            # lister tables
curl -X POST -H "Authorization: Bearer $GRIST_API_KEY" -H "Content-Type: application/json" \
  "$D/tables/<table>/records" -d '{"records":[{"fields":{...}}]}'      # ajouter lignes (endpoint /records)
curl -X POST ... "$D/tables/<table>/data/delete" -d '[id1,id2]'        # supprimer lignes (liste d'ids)
```

**Endpoints clés** : création/lecture lignes = `/tables/{t}/records` ; suppression = `/tables/{t}/data/delete` (body = `[ids]`) ; colonnes = `/tables/{t}/columns` (PATCH pour modifier, `POST` pour ajouter).
**`require` ne déduplique pas** sur `/records` — gérer la déduplication soi-même (contrôle par code avant insertion).

## Organisation (vérifiée)

- **Organisation** : `Personal` (id 2, domaine `docs-5`)
- **Workspace** : `Home` (id 2)
- **Documents** :
  | Document | id (urlId) | Contenu |
  |---|---|---|
  | `MASTER` | `7Zx4iqVUR1uB` | Données principales — tables `PROJECTS`, `CONTACTS`, `INFOS`, `TEST` |
  | `BUDGET` | `8p1hEDPLmWLW` | Suivi budgétaire — tables `Entreprises`, `Projets`, `Marches`, `DC4`, `Lignes_BPU`, … |
  | `PKM` | `pyCAf89kXPiR` | Gestion du PKM (univers/domaines/projets/événements/réunions/tâches/notes/journal) — miroir SilverBullet |
  | `Document sans titre` | `dcLGapaWFaMQ` | À identifier/renommer ou archiver |

## Document PKM (pyCAf89kXPiR) — schéma & conventions

Base de gestion du PKM, alimentée par migration depuis SilverBullet. 8 tables relationnelles, refs via `visibleCol` (les colonnes Ref affichent Nom/Titre, pas l'ID).

| Table | Colonnes | Remarques |
|---|---|---|
| `UNIVERS` | code, nom, couleur | PRO / AMM / PERSO |
| `DOMAINES` | code, nom, univers_id→UNIVERS | 23 |
| `PROJETS` | code, titre, univers_id, domaine_id, statut, lien_sb | 92 ; statut Choice active/…/archived |
| `EVENEMENTS` | date, heure_debut, heure_fin, intitule, projet_id, etat, lien_sb | intitulé **nettoyé** (pas de `- `, `#event`, ni chemin projet) |
| `REUNIONS` | evenement_id→EVENEMENTS, contenu_note, synthese_ia(Bool), synthese_md, lien_sb | **1 ligne / événement** ; pas de date/heure/projet (repris via l'évènement lié) ; synthese_md = texte de la synthèse IA si produite |
| `TACHES` | texte, fait(Bool), date, projet_id, etat, origine | origine = URL page SB |
| `NOTES` | titre, nature, tags, univers_id, page_sb, contenu_md | tags = chaîne "a b c" (jamais de virgules) ; contenu **sans** frontmatter |
| `JOURNAL` | date, lien_sb | 1 colonne lien (pas de doublon) |

**Conventions spécifiques PKM** :
- **Liens SilverBullet** : texte (type Text) avec URL complète `https://sb.datagrandest.net/shizendo/<chemin>`, **sans extension `.md`**. Grist n'a pas de type URL natif → Text, le widget le rend cliquable.
- **`visibleCol`** sur les colonnes Ref pour afficher Nom/Titre (défini à la création du schéma ; le refaire après une re-création de tables).
- **`widgetOptions` des colonnes Choice** : doit être un **JSON en chaîne** `{"choices":[...]}`, pas `choices:[...]` — sinon Grist renvoie `Unexpected token 'c', "choices:["... is not valid JSON` et le document **ne s'ouvre plus** (corruption d'affichage). Piège vécu.
- **`contenu_md` / `contenu_note`** : markdown **sans le bloc frontmatter** `--- ... ---`.
- **Intitulés** : nettoyés à la migration (préfixes, tags, chemins projet retirés).

## Outils MCP disponibles

- `list_organizations`, `list_workspaces`, `list_documents` : explorer la structure.
- `list_tables` (requiert `doc_id`) : lister les tables d'un document.
- Lecture/écriture de lignes, colonnes, tables, webhooks (voir le catalogue MCP pour les signatures exactes).

## Conventions générales

- **Identifiants** : utiliser l'`id` complet (ou `urlId`) du document ; les noms peuvent être ambigus entre documents.
- **Nommage** : `MASTER` = MAJUSCULES (`PROJECTS`) ; `BUDGET` = français (`Entreprises`) ; `PKM` = MAJUSCULES (`UNIVERS`, `DOMAINES`, `PROJETS`, `EVENEMENTS`, `REUNIONS`, `TACHES`, `NOTES`, `JOURNAL`). Respecter le style de chaque document.
- **Ne jamais inventer de données** (règle globale) : ne créer/compléter que ce qui est présent dans les sources.
- **Modification** : lire/vérifier le schéma avant d'écrire/supprimer ; suppressions = destructives → confirmation explicite.
- **Test** : préférer un document jetable ; ne pas créer de données de test dans un document réel sans autorisation.
- **Suppression d'un document** : `DELETE /api/docs/{doc_id}` (workable). Une table vide `Table1` reste systématiquement après création d'un doc par API (non supprimable) — inoffensive.

## Routage

- Conventions de l'espace SilverBullet (différent de Grist) → skill `silverbullet-conventions`
- Accès SilverBullet → skills `silverbullet-api` / `silverbullet-mcp`
- Migration SilverBullet → Grist : scripts de référence `/tmp/opencode/migrate_v3.py` (migration complète), `migrate_pkm.py` (v1), `wipe3.py` (vidage) ; doc d'intervention voir note `VPS-IA_PKM-Grist-2026-11-04`