Aller au contenu

Concepts clés

ConceptEn un mot
WorkbookConteneur de travail (≠ fichier). Regroupe tables, sheets, versions et politiques
SourceL’original uploadé (immutable). auto (tout automatique) / staged (contrôle explicite : analyze → correction du parse-spec → materialize)
TableJeu de données typé, avec ID de ligne, versionné. La cible principale des agents. Deux sortes : base (éditable) et derived (sortie d’un transform)
TransformTemplate SQL (Jinja2) / Python. Lie {{ arg }} aux tables d’entrée via args et produit un artifact de sortie. L’unité de lineage
SheetComposition d’affichage sans données propres. Les blocks référencent des tables (n:m), et le render les compose en xlsx
ChartChart créé par l’agent. Porte une config et une recipe (l’outil et les paramètres de sa génération)
Versionversions.commit(label) = label immuable sur un snapshot. Le revert restaure les tables dérivées via le mécanisme de snapshot (ré-exécution du DAG) — les éditions de lignes des tables base sont, elles, historisées côté log d’edits
JobHandle d’une opération asynchrone (upload async=true, etc.). jobs.wait() ou le webhook job.completed
WorkspaceL’objet frontière (membres, facturation, gouvernance). Un développeur gère plusieurs workspaces avec une Account API key

Toute écriture exige expected_version (l’etag de la table). En cas de décalage : 409 — le SDK lève une ConflictError. Aucun retry automatique (pour ne jamais écraser en silence une édition concurrente) : le contrat est relire (rows() renvoie edit_version) → réappliquer → réessayer.

page = client.tables.rows(wb, "sales")
client.tables.upsert_rows(
wb, "sales",
rows=[{"__d2b_row_id": 3, "amount": 999}], # row_id present = update, absent = insert
expected_version=page["edit_version"],
)

Le SDK ajoute automatiquement Idempotency-Key à tout appel de mutation (même clé + même corps = rejeu de la première réponse). Un retry réseau ne cause jamais de double application. Si vous appelez l’API HTTP directement, ajoutez le header vous-même.

Modèle de promotion (le raw ne disparaît pas en silence)

Section intitulée « Modèle de promotion (le raw ne disparaît pas en silence) »

L’ingestion auto construit les tables structurées de D2B à partir d’une copie raw fidèle de chaque feuille. Les tables structurées prennent le nom du fichier et les tables raw sont rétrogradées en state=archived (le registre et le lineage restent). tables.lineage() remonte jusqu’aux tables raw, et tables.unarchive() rematérialise la feuille d’origine au besoin. Avec structuring=defer, les tables raw sont posées d’abord et la même promotion a lieu quand la structuration se termine ; si des dérivés ont été construits sur une table raw entre-temps, la promotion est automatiquement abandonnée (l’aval n’est pas cassé).

Les écritures interactives, comme l’édition de lignes, ne recalculent pas immédiatement les tables dérivées en aval : elles les marquent stale. freshness: {stale, stale_since} de GET .../tables/{name} l’indique, et POST /workbooks/{id}/recompute (MCP recompute_stale) recalcule l’ensemble dans l’ordre des dépendances. L’exécution d’un transform, elle, se propage de façon eager (sa sortie est toujours fraîche).

Les politiques mask / deny (tag de colonne × rôle) sont imposées par la couche de données. Les colonnes masquées reviennent en NULL typés, signalées par masked_columns. GET .../tables/{name}/access permet un dry-run préalable. La même politique vaut pour l’export, le profile, MCP et le preview de fichiers bruts — sans voie de contournement (sur un workbook gouverné, le preview / revise des octets bruts est refusé).

JSON Schema de ligne (pour le constrained decoding)

Section intitulée « JSON Schema de ligne (pour le constrained decoding) »

Un OpenAPI statique ne peut pas typer le contenu des tables (la forme d’une ligne dépend des données). Chaque table expose donc à l’exécution le « JSON Schema d’une ligne » :

GET /api/v1/workbooks/{wb}/tables/{name}/schema?format=json-schema

Côté MCP : get_schema(..., include_json_schema=true). Le schema renvoyé est généré depuis l’ensemble des colonnes après application des politiques (les colonnes deny n’y apparaissent pas), avec additionalProperties: false, toutes les cellules nullable, et __d2b_row_id en « omis = insert / présent = update ». Un harnais d’agent peut s’en servir pour contraindre la génération du payload d’upsert_rows (constrained decoding).

  • snapshot : commit immuable. POST /snapshots / POST /snapshots/{id}/restore
  • version : un nom sur un snapshot (POST /versions, POST /versions/{label}/revert)
  • op log : toutes les mutations, enregistrées dans l’ordre (GET /ops). Un op de ligne s’inverse via POST /ops/{id}/undo (l’historique est append-only)
  • branch / merge : POST /export avec record_branch=true fige les lignes au moment de l’export, puis POST /tables/{name}/merge applique au fichier édité un 3-way merge cellule par cellule sur la clé d’ID de ligne. Les cellules modifiées des deux côtés partent en queue de conflicts