Skip to content

Symétrie CLI ↔ Portail ​

GISPulse expose deux UIs équivalentes sur la même source de vérité (triggers.yaml + change-log SQLite/PostGIS) : un CLI terminal-first pour les power users et un portail web visual-first pour l'onboarding. Cette page est le test d'invariance : toute feature publique doit apparaître dans les deux colonnes — sinon la dette UX est loggée explicitement.

Doctrine produit confirmée 2026-04-30. Aucun plugin GIS-client requis : QGIS save, ogr2ogr, ArcGIS Pro export, raw sqlite3, CLI ou portail web — tout DML fire les triggers via le change-log. Voir Architecture.

Légende des statuts

StatutSignification
✅Symétrique : feature présente côté CLI et côté portail
⚠️Asymétrique : présent d'un seul côté, dette UX loggée (voir issue)
❌Reporté : pas implémenté ni côté CLI ni côté portail (voir milestone)
🔧Surface "ops" volontairement CLI-only (pas de UI prévue)

1. Rules — CRUD pipelines ​

Source de vérité : règles JSON / YAML chargées via rules.loader. API : rules_router.py.

CapabilityCLIPortailStatut
Créer une règlegispulse template use <preset> (scaffold) puis édition manuelle JSONRuleEditorModal (drag-and-drop registry → schema-driven form) — components/rules/RuleEditorModal.tsx, NodeEditor.tsx✅
Lister les règles d'un pipelinegispulse capabilities (registry) + lecture du JSONNodeEditor workspace — affiche le DAG du pipeline, registry-driven palette✅
Éditer une règleÉdition manuelle du JSON + gispulse validateNodePropertyPanel (form schema-driven) + validation live — components/nodes/NodePropertyPanel.tsx✅
Supprimer une règleSuppression manuelle JSONSuppression node depuis NodeEditor (Delete key / context menu)✅
Valider un pipelinegispulse validate <rules.json>Auto-validate au save dans NodeEditor (POST /rules/{id}/validate)✅
Convertir règle ↔ nodeN/A (le CLI manipule du JSON brut)GET /rules/{id}/to-node + POST /rules/from-node exposés à NodeEditor⚠️
Exécuter un pipelinegispulse run <input> --rules <pipeline.json> -o <output>WorkflowsView → "Run" button (POST /pipelines/execute)✅
Exporter le pipeline en YAML triggersN/A — le CLI consomme directement YAMLN/A — le portail écrit du YAML pour le runtime🔧

Asymétries loggées :

  • ⚠️ rule ↔ node converter : exposé seulement via API REST, pas de commande CLI dédiée. → suggérer issue feat(cli): gispulse rules to-node / from-node (v1.6+).

2. Triggers — Configuration et runtime ​

Source de vérité : YAML triggers + _gispulse_change_log table. API : triggers_router.py. Code CLI : cli_triggers.py, cli_watch.py.

CapabilityCLIPortailStatut
Créer un triggerÉdition manuelle YAMLTriggerBuilderInline / TriggerBuilderModal — PredicateBuilder + ActionEditor + CronBuilder (POST /triggers)✅
Lister les triggersgispulse triggers list --gpkg <path> (triggers SQLite installés)GET /triggers — ScenariosPanel / TriggerHistoryPanel✅
Éditer un triggerÉdition manuelle YAMLTriggerBuilderModal (PUT /triggers/{id})✅
Supprimer un triggerÉdition manuelle YAML + gispulse triggers validateDELETE /triggers/{id} depuis ScenariosPanel✅
Activer / désactiverN/A (commenter dans YAML)POST /triggers/{id}/toggle (UI switch dans TriggerBuilderInline)⚠️
Valider un YAML triggersgispulse triggers validate --config <yaml> --gpkg <path>Validation live au save dans TriggerBuilderModal (réutilise validate_against_gpkg)✅
Tick unique (run-once)gispulse triggers run --config <yaml> --oncePOST /triggers/{id}/evaluate — bouton "Test" dans TriggerBuilderInline✅
Daemon long-runninggispulse triggers run --config <yaml> --watch ou gispulse watch <gpkg> -r <rules>N/A — le portail config un trigger, le runtime local (CLI ou daemon) l'exécute🔧
Stream événements livegispulse triggers run --watch (logs JSON stderr)GET /triggers/eval-stream (SSE) consommé par TriggerHistoryPanel + ActivityTimeline✅
Dryrun (preview actions)N/A — le mode --once exécute pour de vraiPOST /examples/{id}/triggers/dryrun — préview actions sans persister (Mode 2 Try-it)⚠️
Inspecter operations d'un triggerN/AGET /triggers/{id}/operations — historique d'exécution dans TriggerHistoryPanel⚠️

Asymétries loggées :

  • ⚠️ toggle CLI : pas de gispulse triggers enable/disable <id>. → suggérer issue feat(cli): gispulse triggers toggle <id> --enabled/--disabled (v1.6+).
  • ⚠️ dryrun CLI : pas d'équivalent CLI à POST /examples/{id}/triggers/dryrun. → suggérer issue feat(cli): gispulse triggers run --dry-run (v1.6+, déférable).
  • ⚠️ operations history CLI : pas de gispulse triggers history <id>. → suggérer issue feat(cli): gispulse triggers history <id> (v1.6+).

3. Tracking — Change-log SQLite ​

Source de vérité : _gispulse_change_log table dans le GPKG. Code CLI : cli_track.py. API : datasets_router.py (enable_tracking / disable_tracking / tracking_status) + watchers_router.py (dashboard).

CapabilityCLIPortailStatut
Installer le tracking sur une layergispulse track install <gpkg> --layer <name>POST /datasets/{id}/enable_tracking — bouton "Enable tracking" dans DatasetCard✅
Installer sur toutes les layersgispulse track install <gpkg> --all-layersPOST /datasets/{id}/enable_tracking (pas de toggle "all")⚠️
Désinstaller le trackinggispulse track uninstall <gpkg> --layer <name>POST /datasets/{id}/disable_tracking — bouton dans DatasetContextMenu✅
Lister layers trackedgispulse track list <gpkg> (triggers + pending counts)GET /datasets/{id}/tracking_status — affiché dans DatasetCard✅
Lister watchers actifs (dashboard)gispulse watch --status (à shipper v1.6+)GET /watchers — /runtime page (#95)✅ portail
Détail d'un watcher (counters runtime)N/AGET /watchers/{dataset_id} — counters tick/fire/error + last_*_at✅ portail
Tail des changements pendinggispulse track tail <gpkg> --limit 50N/A — ActivityTimeline consomme les events post-dispatch, pas le raw change-log⚠️
Diagnostic + auto-fixgispulse track doctor <gpkg> [--auto-fix]N/A⚠️
Diagnostic global envgispulse doctorN/A — surface "ops" CLI-only volontaire🔧

Asymétries loggées :

  • ⚠️ all-layers UI : le bouton enable_tracking traite une seule layer à la fois. → suggérer issue feat(portal): bulk enable tracking from DatasetCard (v1.6+).
  • ⚠️ tail change-log : utile en debug, pas de panel UI. → suggérer issue feat(portal): raw change-log inspector panel (v1.6+, déférable).
  • ⚠️ track doctor UI : healthcheck triggers + auto-fix à exposer dans DatasetCard. → suggérer issue feat(portal): tracking health badge + repair action (v1.6+).
  • ⚠️ CLI watcher status : GET /watchers shippé côté portail (#95) ; le complément CLI gispulse watch --status (afficher les watchers actifs lus depuis la base/registry partagé) reste à drafter v1.6+.

4. Datasets — Upload, listing, suppression ​

API : datasets_router.py, portal_upload_router.py.

CapabilityCLIPortailStatut
Uploader un dataset (local file)gispulse run consomme directement un fichier localCatalogImportDialog + DragDropOverlay (POST /datasets/upload)⚠️
Uploader depuis URLN/APOST /datasets/import-url — input dans CatalogImportDialog⚠️
Importer depuis OGC API FeaturesN/APOST /datasets/ogc — CatalogPanel connector⚠️
Lister datasetsgispulse layers <file> (single-file) ; gispulse info <file>GET /datasets → DatasetsView + DatasetCard grid⚠️
Inspecter métadonnées (CRS, layers, styles)gispulse info <file>GET /datasets/{id} → InspectorPanel + DatasetSchemaGraph✅
Supprimer un datasetN/A — rm <file> à la mainDELETE /datasets/{id} — DatasetContextMenu⚠️
Renommer un datasetN/APATCH /datasets/{id} → RenameDialog⚠️
Exporter en GPKGgispulse run -o <output.gpkg> (output d'un pipeline)POST /datasets/export-gpkg — bouton "Export" dans DatasetCard✅
Exporter (autres formats)gispulse run -o <output.{geojson,shp,parquet,fgb,...}>POST /datasets/export (16+ formats — voir Formats I/O)✅

Asymétries loggées :

  • ⚠️ dataset registry CLI-side : les datasets sont implicites côté CLI (un fichier sur disque) vs explicites côté portail (registry persistant). → ce design gap est volontaire pour Mode 1, mais on pourrait exposer gispulse datasets list/add/rm qui pointe sur un registre local optionnel. À débattre v1.6+ — issue feat(cli): optional dataset registry.
  • ⚠️ import-url / OGC CLI : pas de gispulse import url <URL> ni gispulse import ogc <endpoint>. → suggérer issue feat(cli): gispulse import (v1.6+).

5. Examples — Mode 2 portail "Try it" ​

API : examples_router.py. Sprint v1.5.1, registry de datasets fixes read-only.

CapabilityCLIPortailStatut
Lister les exemples disponiblesN/A — surface volontairement portail-only (Mode 2 Community demo)GET /examples → MarketplacePage + landing🔧
Détails d'un exempleN/AGET /examples/{id} → preview card🔧
Preview tile / MVTN/A — le viewer consomme directement le GPKG localGET /examples/{id}/preview + /examples/{id}/tiles/{z}/{x}/{y}.mvt → MapView🔧
Dryrun triggers sur exemplegispulse triggers run --once --config <yaml> --gpkg <example.gpkg> (en local, après pipx install gispulse)POST /examples/{id}/triggers/dryrun — TriggerBuilderModal "Test on this example"✅

Surface "Try it" : par construction le portail propose les exemples comme on-ramp vers pipx install gispulse. Les CLI users qui clonent le repo ont accès aux mêmes datasets via examples/. Pas de dette UX ici — c'est le funnel.


6. Styles — QML / SLD roundtrip ​

API : portal_datasets_router.py (styles import / export / breaks). Sprint v1.5.0.

CapabilityCLIPortailStatut
Importer un style QMLN/A — QML déjà copié par gispulse run --all-layersPOST /datasets/{id}/styles/import — LayerColorPicker / SchemaView action⚠️
Exporter un style QMLgispulse run copie automatiquement les styles depuis le GPKG d'entréeGET /datasets/{id}/styles → bouton "Download QML"✅
Mettre à jour le styleN/APUT /datasets/{id}/styles — LayerColorPicker + MapLegend editing⚠️
Calculer des breaks (Jenks / quantile / equal interval)N/APOST /datasets/{id}/layers/{layer}/breaks — LayerColorPicker classification picker⚠️
Lister les valeurs distinctes d'un champN/AGET /datasets/{id}/layers/{layer}/distinct/{field}⚠️
Stats descriptives (min/max/mean/quantiles)N/AGET /datasets/{id}/layers/{layer}/stats/{field} — InspectorPanel⚠️

Asymétries loggées :

  • ⚠️ styles CLI : import / classify breaks / stats sont des opérations cartographiques par essence visuelles. CLI symétrique faible utilité. → loggable comme issue non-prioritaire feat(cli): gispulse style classify --field <f> --method jenks --bins 5 pour CI / batch. v1.7+.

7. Run — Exécution pipelines ​

Source de vérité : core.pipeline + orchestration.session_manager. API : pipelines_router.py, jobs_router.py.

CapabilityCLIPortailStatut
Exécuter un pipeline (sync)gispulse run <input> --rules <pipeline.json> -o <output>POST /pipelines/execute — WorkflowsView "Run"✅
Exécuter étape par étapeN/A (pas d'API CLI dédiée — l'engine exécute en bloc)POST /pipelines/execute-steps — debug mode dans NodeEditor⚠️
Valider un pipelinegispulse validate <pipeline.json>POST /pipelines/validate — auto-validate au save✅
Lister les jobsgispulse jobs list [--host HOST] [--api-key KEY]GET /jobs → JobTrackerCorner (lazy panel)✅
Statut d'un jobgispulse jobs status <JOB_ID>GET /jobs/{id} → JobTrackerCorner détail✅
Stream events d'un jobN/A (le CLI exécute sync, pas de SSE)GET /jobs/{id}/events (SSE) → progress dans JobTrackerCorner⚠️
Annuler un jobgispulse jobs cancel <JOB_ID>POST /jobs/{id}/cancel → JobTrackerCorner action✅
Télécharger les features d'un jobN/A — output déjà écrit en local par gispulse runGET /jobs/{id}/features + /jobs/{id}/download⚠️
Soumettre un job asyncN/A (gispulse run est synchrone)POST /jobs — submit async via WorkflowsView⚠️
Examples / presets pipelinesgispulse template list + gispulse template use <name>GET /pipelines/examples → palette ou WorkflowList✅

Asymétries loggées :

  • ⚠️ execute-steps CLI : utile pour debug step-by-step. → suggérer issue feat(cli): gispulse run --step <id> (v1.7+, déférable).
  • ⚠️ jobs SSE / async CLI : gispulse run est synchrone par design (script-friendly). Le pattern async est portail-only, justifié pour un workflow long-running. Pas d'urgence.

8. Schedules — Cron jobs ​

API : schedules_router.py. Composant : components/schedules/ScheduleForm.tsx.

CapabilityCLIPortailStatut
Créer un scheduleN/A — utiliser cron / systemd timers natifs OS pour wrapper gispulse runPOST /schedules → ScheduleForm (CronBuilder réutilisé depuis triggers)⚠️
Lister schedulesN/AGET /schedules⚠️
Détails / éditer scheduleN/AGET / PATCH /schedules/{id}⚠️
Supprimer scheduleN/ADELETE /schedules/{id}⚠️
Run-now manuelgispulse run directPOST /schedules/{id}/run-now⚠️

Asymétries loggées :

  • ⚠️ schedules CLI absent : décision produit à confirmer — soit on assume "use cron" pour CLI users, soit on expose gispulse schedules add/list/rm. → suggérer issue decision: gispulse schedules CLI subcommand (v1.6+).

9. Marketplace — Plugins / capabilities tierces ​

API : marketplace_router.py. Composants : components/marketplace/, pages/MarketplacePage.tsx.

CapabilityCLIPortailStatut
Lister plugins installésgispulse marketplace list [QUERY]GET /marketplace/plugins → MarketplacePage✅
Rechercher dans le cataloguegispulse marketplace search QUERYGET /marketplace/search + /marketplace/catalog✅
Détails d'un plugingispulse marketplace info NAMEGET /marketplace/plugins/{name}✅
Installer un plugingispulse marketplace install NAMEPOST /marketplace/install✅
Désinstaller un plugingispulse marketplace uninstall NAMEDELETE /marketplace/plugins/{name}✅

✅ Symétrie complète. Surface marketplace alignée par construction depuis v1.1.0.


10. Templates — Scaffolding projets ​

API : pipelines_router.py /examples. CLI : gispulse template.

CapabilityCLIPortailStatut
Lister templatesgispulse template listGET /pipelines/examples (preset library exposé dans WorkflowList)✅
Scaffolder un projet depuis templategispulse template use <NAME> [--output-dir DIR]OnboardingFlow (premier lancement) + SaveTemplateDialog✅
Créer un workflow depuis templategispulse template workflowWorkflowList → "From template"✅

✅ Symétrie complète.


11. Viewer / Portal / Engine — Lifecycle process ​

Surface "ops" — comment lancer GISPulse.

CapabilityCLIPortailStatut
Lancer le viewer (read-only)gispulse serve <file> [--port 8765]N/A — le viewer est intégré dans le portail🔧
Lancer le portailgispulse portal [--port 8001]N/A — le portail est le portail (méta)🔧
Lancer le moteur completgispulse engine [--port 8001] (Tauri sidecar JSON)N/A🔧
Connect "My engine" depuis portail publicgispulse portal --backend=<URL> (Mode 2 — sprint v1.5.1)BackendStatusBanner + SettingsPanel (input URL backend, persist localStorage) — livré gispulse-portal #30✅
Diagnostiquer l'environnementgispulse doctorN/A🔧
Mettre à jourgispulse update [--check] [--force]N/A — le portail web est self-updating, le CLI gère sa propre version🔧
Initialiser un projetgispulse init [DIR] [--name NAME]OnboardingFlow (équivalent visuel pour la première session)✅
Télémétrie opt-ingispulse telemetry --enable / --disable / --statusN/A — config CLI only (env var GISPULSE_TELEMETRY=1 pour scripts)🔧

Surface 🔧 volontaire : le lifecycle process et la télémétrie sont CLI-only par design — le portail est déjà lancé quand l'user clique. Pas de dette.


12. SQL Console — Édition / preview SQL ​

API : portal_sql_router.py. Composant : components/sql/SQLConsole.tsx.

CapabilityCLIPortailStatut
Exécuter une requête SQLN/A — gispulse run accepte le pipeline + capability postgis_sqlPOST /sql/execute → SQLConsole⚠️
Preview résultats SQLN/ASQLPreviewTable (auth + blocklist côté backend, v1.1.0)⚠️
Exporter résultats SQLN/APOST /sql/export⚠️

Asymétries loggées :

  • ⚠️ SQL CLI : fonctionnalité plutôt "exploration interactive" — déjà couvert pour batch via la capability postgis_sql dans un pipeline. Pas urgent. → issue déférable feat(cli): gispulse sql --execute "SELECT ..." (v1.7+).

13. Auth — SSO et identité ​

API : auth_router.py. OSS : stub anonyme. Pro/Enterprise : OIDC (provider Google / Azure / Keycloak — voir gispulse-enterprise).

CapabilityCLIPortailStatut
Lister providers SSON/A (pas d'auth CLI en OSS)GET /auth/providers → pages/auth/🔧
User infoN/AGET /auth/me → UserMenu + AuthGuard🔧

Surface 🔧 : OSS Mode 1 = single-user CLI sans auth. Mode 2 portail SaaS Pro v1.6+ ajoutera l'auth visuelle. CLI auth viendra avec gispulse login (issue v1.7+).


Récapitulatif ​

Domaine✅ Symétrique⚠️ Asymétrique🔧 Volontairement CLI/Portal-only❌ Reporté
Rules7110
Triggers6410
Tracking4310
Datasets3600
Examples1030
Styles1500
Run5400
Schedules0500
Marketplace5000
Templates3000
Lifecycle / Engine2060
SQL0300
Auth0020
Total3731140

Lecture : sur 82 capabilities publiques, 37 sont déjà symétriques, 14 sont CLI-only ou portail-only par design assumé, et 31 dettes UX sont identifiées et listées ci-dessus avec leur issue suggérée. Aucune capability n'est silencieusement absente d'une des deux surfaces.


Comment cette page reste à jour ​

Cette matrice est aujourd'hui maintenue manuellement. Toute nouvelle feature (CLI ou portail) doit être ajoutée à la ligne correspondante avec son statut. Une issue v1.6+ (feat(scripts): generate symmetry.md from CLI ↔ portal mapping) explore une génération automatique à partir d'un mapping déclaratif dans le code source — pour l'instant le contenu manuel reste l'autorité.

Process pour toute nouvelle PR ajoutant une feature :

  1. Identifier la ligne à ajouter / mettre à jour dans cette matrice
  2. Si la PR introduit une asymétrie, logger l'issue de dette correspondante dans la même session
  3. Demander la review de Marco (gis-lead-dev) ou Jordan (jordan-po) pour valider le statut

Voir aussi :

Published under AGPL-3.0 license.