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 / TriggerBuilderModalPredicateBuilder + ActionEditor + CronBuilder (POST /triggers)
Lister les triggersgispulse triggers list --gpkg <path> (triggers SQLite installés)GET /triggersScenariosPanel / 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).

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
Tail des changements pendinggispulse track tail <gpkg> --limit 50N/AActivityTimeline 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+).

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/ogcCatalogPanel connector⚠️
Lister datasetsgispulse layers <file> (single-file) ; gispulse info <file>GET /datasetsDatasetsView + DatasetCard grid⚠️
Inspecter métadonnées (CRS, layers, styles)gispulse info <file>GET /datasets/{id}InspectorPanel + DatasetSchemaGraph
Supprimer un datasetN/Arm <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 /examplesMarketplacePage + 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}.mvtMapView🔧
Dryrun triggers sur exemplegispulse triggers run --once --config <yaml> --gpkg <example.gpkg> (en local, après pipx install gispulse)POST /examples/{id}/triggers/dryrunTriggerBuilderModal "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/importLayerColorPicker / 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}/stylesLayerColorPicker + MapLegend editing⚠️
Calculer des breaks (Jenks / quantile / equal interval)N/APOST /datasets/{id}/layers/{layer}/breaksLayerColorPicker 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/executeWorkflowsView "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 /jobsJobTrackerCorner (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}/cancelJobTrackerCorner 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 /schedulesScheduleForm (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/pluginsMarketplacePage
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/Agispulse run accepte le pipeline + capability postgis_sqlPOST /sql/executeSQLConsole⚠️
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/providerspages/auth/🔧
User infoN/AGET /auth/meUserMenu + 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.