Skip to content

Supreme Graal

CYPT71 edited this page Aug 18, 2026 · 1 revision

TODO — Bidirectional Migration Plugins

Objectif

Faire évoluer Platform Factory pour permettre à un plugin de fonctionner dans une ou deux directions :

native environment
        ↓
     plugin
        ↓
 canonical model

et :

 canonical model
        ↓
     plugin
        ↓
native environment

Un plugin peut donc être :

DISCOVERY ONLY
APPLY ONLY
BIDIRECTIONAL

selon les capabilities réellement implémentées et vérifiées.

Le système doit permettre :

Plugin A
   ↓
discover
   ↓
canonical model
   ↓
Platform Factory
   ↓
capability resolution
   ↓
Plugin B
   ↓
apply

et également l’inverse :

Plugin B
   ↓
discover
   ↓
canonical model
   ↓
Platform Factory
   ↓
capability resolution
   ↓
Plugin A
   ↓
apply

Principe fondamental :

PLUGIN → CAPABILITY

NEVER

PLUGIN → PLUGIN

Le plugin traduit son environnement.

Platform Factory comprend, planifie, résout, orchestre, observe et vérifie.


0. Invariants architecturaux

  • Un plugin ne doit jamais importer un autre plugin
  • Un plugin ne doit jamais invoquer directement un autre plugin
  • Un plugin ne doit jamais sélectionner une autre implémentation par son nom
  • Un plugin externe ne doit pas importer internal/...
  • Les interactions passent par le protocole plugin existant
  • Les plugins sont découverts via internal/plugin.Registry
  • Le host résout les capabilities
  • TraceID est propagé E2E
  • OperationID protège toutes les mutations
  • Les résultats plugins restent non fiables jusqu’à vérification host
  • Le modèle canonique reste indépendant de toute implémentation
  • Aucun nouveau système RPC
  • Aucun deuxième registry
  • Aucun deuxième journal d’idempotence
  • Aucun deuxième modèle de pipeline

Invariant :

PLUGIN ≠ ORCHESTRATOR

1. Étendre le contrat plugin existant

Ne pas créer une nouvelle abstraction parallèle.

Réutiliser :

api/plugin
sdk/plugin
internal/plugin.Registry

Ajouter uniquement les contrats nécessaires pour représenter les nouvelles capabilities.

Un plugin peut annoncer des opérations conceptuellement réparties en deux directions.

Observation

discover
inspect
observe
export
verify

Matérialisation

plan
import
apply
update
delete
verify

La présence d’une capability dans une direction ne doit jamais impliquer l’autre.

Exemple :

plugin A

discover ✓
inspect  ✓
export   ✓
apply    -

ou :

plugin B

discover ✓
inspect  ✓
apply    ✓
delete   ✓
verify   ✓

2. Capability Model

Toutes les décisions doivent être prises par capability.

Interdit :

if plugin.Name == "..."

Correct :

requires:
  runtime.vm.create

ou :

requires:
  deployment.apply

Le host résout ensuite :

requirement
    ↓
Registry
    ↓
candidate plugins
    ↓
trust verification
    ↓
capability verification
    ↓
selected plugin

3. Capability States

Ne pas confondre :

DECLARED
DISCOVERED
NEGOTIATED
VERIFIED
AVAILABLE

Un manifeste peut annoncer une capability.

Cela ne suffit pas à la considérer utilisable.

Flux :

plugin manifest
      ↓
Registry
      ↓
plugin handshake
      ↓
runtime verification
      ↓
AVAILABLE

Invariant :

DECLARED CAPABILITY
≠
AVAILABLE CAPABILITY

4. Modèle canonique

Créer ou compléter les contrats publics nécessaires sous :

api/migration/

Optionnellement :

sdk/migration/

Répartition :

api/migration
→ contrats sérialisables

sdk/migration
→ helpers publics

internal/app/migration
→ orchestration

Ne pas placer un contrat utilisé par un plugin externe dans internal/....


5. Resource Model

Créer un modèle générique.

type Resource struct {
    ID           string
    Kind         string
    Source       ResourceOrigin
    Attributes   map[string]string
    Requirements []Requirement
}
type ResourceOrigin struct {
    PluginID   string
    NativeType string
    NativeID   string
    Location   string
}

Le modèle doit conserver :

  • identité native ;
  • origine ;
  • caractéristiques ;
  • requirements ;
  • garanties ;
  • état observé.

Il ne doit jamais dépendre d’un target particulier.


6. Dependency Graph

Ajouter :

type DependencyEdge struct {
    From     string
    To       string
    Relation string
    Required bool
}

Le graphe doit permettre de répondre :

What depends on this resource?

What must move with it?

What may remain external?

What becomes unavailable if this resource disappears?

Invariants

  • IDs uniques
  • références valides
  • dépendances externes explicites
  • ordre déterministe
  • relations déterministes
  • aucune dépendance silencieusement ignorée
  • détection des incohérences

7. Discovery

Un plugin capable de discovery doit :

  • observer son environnement natif
  • découvrir les ressources
  • découvrir les relations
  • découvrir l’état
  • découvrir les requirements
  • découvrir les garanties
  • signaler les zones non accessibles
  • signaler les données non observables
  • produire un résultat déterministe

Invariant :

NOT OBSERVED
≠
DOES NOT EXIST

8. Discovery Status

Représenter explicitement :

complete
partial
failed

Exemple :

discovery:
  status: partial

  unknowns:
    - scope: network
      reason: permission-denied

    - scope: identity
      reason: unsupported

Un plugin ne doit jamais transformer un échec d’observation en absence de ressource.


9. Canonical YAML

Permettre de sérialiser le modèle canonique en YAML.

Exemple :

apiVersion: platform.factory/v1
kind: ImportedEnvironment

metadata:
  name: production

discovery:
  status: complete

resources:

  - id: app

    kind: container-workload

    source:
      plugin: plugin-a
      nativeType: workload
      nativeID: app-001

    requirements:

      - capability: runtime.container
        required: true

      - capability: network.private
        required: true


graph:

  edges:

    - from: ingress
      to: app
      relation: routes-to
      required: true

Le YAML doit être :

  • déterministe
  • canonique
  • diffable
  • versionné
  • validable
  • sans secrets
  • indépendant des plugins target

10. Gros environnements

Pour les gros environnements, permettre :

environment/
├── environment.yaml
├── resources/
│   ├── compute.yaml
│   ├── workloads.yaml
│   ├── networking.yaml
│   ├── storage.yaml
│   ├── identities.yaml
│   └── services.yaml
├── graph.yaml
├── compatibility.yaml
└── migration-plan.yaml

Le manifest principal doit contenir les digests des documents référencés.

Tâches

  • chemins relatifs uniquement
  • protection path traversal
  • digest de chaque document
  • ordre stable
  • validation à la lecture

11. Validation

Deux niveaux.

Schema Validation

Is the document structurally valid?

Semantic Validation

Does the represented system make sense?

Vérifier :

  • IDs uniques
  • références existantes
  • graphe cohérent
  • capabilities valides
  • requirements cohérents
  • digests valides
  • aucune donnée secrète
  • versions supportées
  • états cohérents

12. Requirements

Les plugins doivent décrire :

WHAT IS REQUIRED

jamais :

WHO MUST IMPLEMENT IT

Exemple :

requirements:

  - capability: runtime.vm.create
    required: true

  - capability: storage.block.import
    required: true

  - capability: network.configure
    required: true

Interdit :

plugin: plugin-b

dans un requirement métier.


13. Compatibility Model

Chaque ressource doit être classée :

DIRECT
ADAPTABLE
DEGRADED
UNSUPPORTED

Créer :

type Compatibility string

const (
    CompatibilityDirect      Compatibility = "direct"
    CompatibilityAdaptable   Compatibility = "adaptable"
    CompatibilityDegraded    Compatibility = "degraded"
    CompatibilityUnsupported Compatibility = "unsupported"
)

Aucune ressource ne doit disparaître silencieusement.


14. Compatibility Gap

Créer :

type CompatibilityGap struct {
    ResourceID      string
    Requirement     string
    Status          Compatibility
    Reason          string
    LostGuarantee   string
    RequiresApproval bool
}

Exemple :

compatibility:

  status: degraded

  lostGuarantees:
    - high-availability

  reason: >
    Target environment does not expose the required
    redundancy capability.

  requiresApproval: true

15. MigrationPlan

Créer ou compléter :

type MigrationStep struct {
    ID             string
    Requirements   []Requirement
    SourceResource string
    DependsOn      []string
    OperationID    string
}
type MigrationPlan struct {
    Version     string
    Resources   []Resource
    Graph       []DependencyEdge
    Steps       []MigrationStep
    Gaps        []CompatibilityGap
    Unknowns    []UnknownObservation
    Digest      string
}

Invariant :

MIGRATION PLAN
MUST NOT CONTAIN
TARGET PLUGIN NAMES

16. MigrationPlan Determinism

Garantir :

same canonical input
→ same MigrationPlan

Tâches

  • ordre stable
  • IDs stables
  • sérialisation canonique
  • digest du plan
  • aucune timestamp non nécessaire
  • aucune dépendance à l’ordre de découverte

17. Host Orchestration

Créer ou compléter :

internal/app/migration/

Responsabilités :

  • charger le modèle canonique
  • valider
  • construire ou charger le MigrationPlan
  • résoudre les capabilities
  • sélectionner les plugins compatibles
  • vérifier la confiance
  • démarrer les plugins
  • propager TraceID
  • attribuer OperationID
  • gérer le DAG
  • gérer retries
  • observer après mutation
  • vérifier la convergence
  • produire provenance

Le host est le seul orchestrateur.


18. Interdiction plugin → plugin

Ajouter explicitement dans internal/archtest :

PLUGIN
MUST NOT IMPORT
ANOTHER PLUGIN

et :

EXTERNAL PLUGIN
MUST NOT IMPORT
internal/...

Le host doit être la seule couche connaissant plusieurs plugins.


19. Plugin Discovery Direction

Un plugin source doit pouvoir fonctionner même sans aucun target plugin installé.

Il doit toujours pouvoir, si ses capabilities le permettent :

discover
inspect
normalize
validate

Tester explicitement ce cas.


20. Plugin Apply Direction

Un plugin capable de matérialisation reçoit :

canonical resource
+
requirements
+
OperationID
+
TraceID

Puis :

plan native operation
→ apply
→ observe
→ normalize observed state
→ verify

Ne jamais considérer le retour de l’API native comme preuve suffisante.


21. Observe After Apply

Flux obligatoire :

apply
 ↓
native system accepts request
 ↓
observe
 ↓
normalize
 ↓
compare with desired
 ↓
verify

Invariant :

REQUEST ACCEPTED
≠
DESIRED STATE VERIFIED

et :

COMMAND SUCCESS
≠
SYSTEM SUCCESS

22. Reconciliation

Lorsque l’état observé diffère de l’état voulu :

desired
 ↓
observed
 ↓
difference
 ↓
action
 ↓
observe again

Le système doit :

  • converger ;
  • réduire la différence ;
  • ou produire une erreur terminale explicite.

Éviter les retries infinis sans progression.


23. Bidirectional Plugins

Un même plugin peut exposer :

discover
inspect
export

et :

import
apply
observe
verify

Le host doit traiter chaque capability indépendamment.

Ne jamais considérer :

plugin supports discover

comme preuve qu’il supporte :

apply

24. Round-Trip Validation

Ajouter :

Native A
 ↓
Plugin A
 ↓
Canonical
 ↓
Plugin B
 ↓
Native B
 ↓
Plugin B discover
 ↓
Canonical

Comparer :

required guarantees before
vs
observed guarantees after

Ne pas exiger une égalité byte-for-byte.

Comparer les propriétés nécessaires.


25. Round-Trip Properties

Tester :

  • CPU
  • mémoire
  • architecture
  • firmware
  • disks
  • stockage
  • réseau
  • ports
  • persistence
  • health
  • identité
  • secrets requirements
  • availability
  • isolation
  • durability

Toute différence non acceptable doit produire un CompatibilityGap.


26. Export / Import Artifacts

Pour les plugins manipulant des artefacts :

native artifact
 ↓
plugin export
 ↓
portable artifact
 ↓
host verification
 ↓
plugin import

Le host doit vérifier :

  • digest ;
  • taille ;
  • format ;
  • structure ;
  • provenance.

Invariant :

PLUGIN OUTPUT
≠
TRUSTED ARTIFACT

27. Credentials

Séparer les scopes.

Discovery Credentials
→ READ ONLY
Export Credentials
→ restricted source mutation
Apply Credentials
→ target mutation

Un plugin bidirectionnel ne doit pas demander par défaut des credentials omnipotents.

Le jeu de permissions doit dépendre de l’opération.


28. Secret Handling

  • jamais dans YAML
  • jamais dans MigrationPlan
  • jamais dans logs
  • jamais dans provenance
  • jamais dans cache plaintext
  • jamais dans diagnostics
  • références uniquement lorsque possible

Le target doit résoudre ou injecter les secrets selon les capabilities disponibles.


29. TraceID

Propager :

CLI
 ↓
migration host
 ↓
source plugin
 ↓
artifact operations
 ↓
target plugin
 ↓
native environment
 ↓
verification
 ↓
provenance

Un workflow complet doit être retraçable.


30. OperationID

Toute mutation doit utiliser :

TraceID
OperationID

Invariant :

same OperationID
→ no duplicate logical effect

Tester :

  • duplicate call
  • duplicate concurrent call
  • retry
  • timeout
  • crash
  • reconnect
  • indeterminate operation
  • reconciliation

31. Operation Journal

Réutiliser les journaux existants.

Ne pas créer un troisième mécanisme.

Vérifier la distinction entre :

in-memory journal

et :

persistent operation journal

Ne pas déclarer une opération crash-safe si son état n’est pas persisté.


32. Sandbox

Un plugin reçoit uniquement :

  • filesystem nécessaire ;
  • réseau nécessaire ;
  • credentials nécessaires ;
  • workspace ;
  • budgets nécessaires.

Interdit :

  • accès hôte arbitraire ;
  • credentials d’un autre environnement ;
  • runtime socket sans capability explicite ;
  • accès direct à un autre plugin.

Si le niveau d’isolation requis n’est pas disponible :

FAIL

sauf policy explicite autorisant une dégradation.


33. Provenance

Inclure :

  • source plugin ID
  • source plugin digest
  • target plugin ID
  • target plugin digest
  • canonical graph digest
  • MigrationPlan digest
  • source resource IDs
  • target resource IDs
  • artifact digests
  • transformations
  • compatibility gaps
  • capabilities demandées
  • capabilities résolues
  • capabilities vérifiées
  • TraceID
  • OperationIDs
  • observations
  • verification results
  • final state

Invariant :

PROVENANCE
=
WHAT ACTUALLY HAPPENED

34. CLI Générique

Prévoir une UX générique.

pf migrate discover \
  --plugin plugin-a

Puis :

pf migrate validate environment/

Puis :

pf migrate inspect environment/

Puis :

pf migrate plan \
  --from plugin-a \
  --to plugin-b

Puis :

pf migrate apply migration-plan.yaml

Les noms servent ici à la sélection utilisateur explicite.

La logique métier du plan reste basée sur les capabilities.


35. Auto Target Resolution

Permettre également :

pf migrate plan \
  --from plugin-a \
  --target auto

Le host :

requirements
 ↓
Registry
 ↓
compatible plugins
 ↓
verified capabilities
 ↓
compatibility score
 ↓
selected target

La décision doit être explicable.


36. Dry Run

Les opérations suivantes doivent pouvoir être sans mutation :

discover
inspect
validate
plan
compatibility

Invariant :

PLAN
≠
APPLY

Aucun plan ne doit provoquer de mutation cachée.


37. Tests de contrat plugin

Chaque plugin bidirectionnel doit passer une suite commune.

Discovery

  • empty environment
  • normal environment
  • partial discovery
  • permissions denied
  • malformed native data
  • pagination
  • cancellation
  • timeout

Apply

  • create
  • update
  • duplicate OperationID
  • timeout
  • partial success
  • native rejection
  • observe after apply
  • verify

38. Tests d’indépendance

Tester :

Plugin A works without Plugin B
Plugin B works without Plugin A
Plugin A never imports Plugin B
Plugin B never imports Plugin A

39. Tests Capability Resolution

Cas :

  • aucune implémentation
  • une implémentation
  • plusieurs implémentations
  • capability déclarée mais indisponible
  • plugin non vérifié
  • plateforme incompatible
  • permissions insuffisantes
  • target degraded

40. Tests Round-Trip

Créer des fixtures génériques :

fixture A
 ↓
Plugin A normalize
 ↓
Canonical
 ↓
Fake Plugin B apply
 ↓
Fake Plugin B discover
 ↓
Canonical

Comparer les requirements et garanties.

Toute perte doit être explicitement déclarée.


41. Tests Fault Injection

Injecter :

  • source plugin crash
  • target plugin crash
  • host crash
  • network failure
  • partial artifact
  • partial mutation
  • timeout
  • disk full
  • verification failure
  • stale observation
  • duplicate operation

Invariant :

FAILURE
MUST NEVER BECOME
FALSE SUCCESS

42. Fuzzing

Priorités :

  • canonical model parser
  • YAML
  • graph
  • MigrationPlan
  • plugin messages
  • compatibility data
  • artifact metadata
  • provider-native normalization inputs

Toute découverte de crash devient regression test.


43. Architecture Tests

Ajouter :

PLUGIN MUST NOT IMPORT ANOTHER PLUGIN
PLUGIN MUST NOT IMPORT internal/...

pour les modules externes.

api/migration MUST NOT IMPORT internal/...
sdk/migration MUST NOT IMPORT internal/...
cmd/platform-factory MUST NOT IMPLEMENT PLUGIN-SPECIFIC DOMAIN LOGIC

L’architecture test doit fail closed.


44. Ordre d’implémentation

Jalon 1 — Canonical Model

native
→ plugin
→ canonical
  • Resource
  • Requirements
  • DependencyEdge
  • Compatibility
  • UnknownObservation
  • validation
  • serialization

Jalon 2 — Plugin Discovery Contract

plugin
→ discover
→ normalize
→ canonical graph
  • RPC
  • capability
  • TraceID
  • tests
  • partial discovery

Jalon 3 — MigrationPlan

canonical graph
→ compatibility
→ requirements
→ DAG
  • deterministic steps
  • OperationIDs
  • digest
  • no plugin names
  • tests

Jalon 4 — Target Capability Resolution

MigrationPlan
→ Registry
→ verified target plugin
  • candidate resolution
  • capability verification
  • compatibility
  • explainable selection

Jalon 5 — Apply Contract

canonical
→ plugin
→ native
  • apply
  • observe
  • normalize
  • verify
  • idempotence
  • provenance

Jalon 6 — Bidirectional Plugin

Faire fonctionner un plugin dans les deux directions :

Native
 ↕
Plugin
 ↕
Canonical
  • discover
  • inspect
  • export
  • import
  • apply
  • observe
  • verify

Jalon 7 — Two-Plugin Migration

Démontrer :

Plugin A
 ↓
Canonical
 ↓
Platform Factory
 ↓
Plugin B

avec :

  • discovery
  • graph
  • validation
  • plan
  • capability resolution
  • apply
  • observe
  • verify
  • provenance

Jalon 8 — Reverse Direction

Démontrer :

Plugin B
 ↓
Canonical
 ↓
Platform Factory
 ↓
Plugin A

sans introduire de code spécifique à la paire A/B.


Jalon 9 — Auto Resolution

Ajouter un Plugin C.

Vérifier que :

A ↔ C
B ↔ C

devient possible sans modifier A ou B.


45. Critère Meine Graal

Meine Graal est atteint lorsque l’ajout d’un nouveau plugin compatible ne nécessite aucune modification spécifique aux plugins déjà présents.

Test :

Add Plugin X

sans modifier :

Plugin A
Plugin B
Plugin C

et obtenir automatiquement, lorsque les capabilities le permettent :

X ↔ A
X ↔ B
X ↔ C

La propriété recherchée est :

COST OF ADDING A PLUGIN
≈ O(1)

et non :

COST OF ADDING A PLUGIN
≈ NUMBER OF EXISTING PLUGINS

46. Estimation d’effort

L’évolution ne nécessite pas une réécriture du projet.

Les fondations existent déjà :

plugin protocol       ✓
plugin Registry       ✓
capabilities          ✓
TraceID               ✓
OperationID           ✓
provenance            ✓
architecture tests    ✓
runtime abstractions  ✓

Contrats génériques

0.5 – 1 jour
  • canonical migration types
  • bidirectional capabilities
  • compatibility
  • tests architecture

Orchestration générique

1 – 2 jours
  • source plugin
  • target resolution
  • MigrationPlan
  • apply
  • observe-after-apply
  • verification

Adaptation d’un plugin existant

1 – 2 jours
  • discovery
  • normalization
  • apply contract
  • tests

Premier vertical bidirectionnel

2 – 4 jours
  • discover
  • canonicalize
  • plan
  • resolve
  • apply
  • observe
  • verify

E2E

1 – 2 jours
  • A → B
  • B → A
  • fault injection
  • provenance

Estimation globale

Adaptation architecturale

2 – 3 jours

Premier vertical fonctionnel

5 – 10 jours

Version fortement durcie

3 – 6 semaines

selon :

  • profondeur des APIs natives ;
  • stockage ;
  • réseau ;
  • sécurité ;
  • conversion d’artefacts ;
  • edge cases ;
  • rollback ;
  • tests adversariaux.

47. Definition of Done

Le système de plugins bidirectionnels est considéré terminé lorsque :

  • un plugin peut découvrir son environnement
  • discovery peut être read-only
  • un plugin peut produire le modèle canonique
  • le modèle canonique est déterministe
  • le graphe de dépendances est explicite
  • les observations inconnues restent inconnues
  • le MigrationPlan est indépendant des plugins
  • le host résout les capabilities
  • les capabilities sont vérifiées
  • un plugin peut matérialiser un modèle canonique
  • apply est idempotent
  • observe-after-apply est obligatoire
  • la convergence est vérifiée
  • les pertes de garanties sont explicites
  • round-trip validation fonctionne
  • TraceID est propagé
  • OperationID protège les mutations
  • provenance décrit le workflow réel
  • aucun plugin n’importe un autre plugin
  • aucun nouveau framework parallèle n’a été introduit
  • architecture tests passent
  • make verify passe
  • make release-check passe

Règle finale

NATIVE ENVIRONMENT
        ↓
      PLUGIN
        ↓
     OBSERVE
        ↓
 CANONICAL MODEL
        ↓
     VALIDATE
        ↓
       PLAN
        ↓
PLATFORM FACTORY
        ↓
RESOLVE CAPABILITIES
        ↓
      PLUGIN
        ↓
       APPLY
        ↓
      OBSERVE
        ↓
      VERIFY
        ↓
    PROVENANCE

Et toujours :

PLUGIN → CAPABILITY

jamais :

PLUGIN → PLUGIN

Le plugin connaît son monde.

Platform Factory connaît le modèle, les règles, les capabilities et l’orchestration.

Aucun plugin n’a besoin de connaître l’existence des autres.

Clone this wiki locally