Artefacts d'exécution

Un artefact d'exécution est quelque chose qu'une exécution de workflow a produit et qu'une personne peut regarder — une application web construite ou un APK Android — rendu à l'intérieur de la conversation qui possède la mission, avec une identité durable qui survit à toute session de visualisation. L'exécution construit la chose ; Genesis la sert sur notre propre infrastructure et diffuse des pixels vers le navigateur de l'opérateur, de sorte que les clics de l'opérateur sont de vrais clics sur l'application en cours d'exécution tandis que son code n'atteint jamais sa machine.

Les artefacts d'exécution sont par locataire : chaque outil et point de terminaison est protégé par la politique tenant-admin, et la location est implicite via la base de données par locataire (pas de colonne TenantId — ADR-008), exactement comme les missions, les tâches et les workflows. L' enregistrement vit dans la base de données du locataire propriétaire ; un identifiant frappé pour un locataire est structurellement invisible pour un autre.

Cette page couvre les deux surfaces :

  • Opérateur / produit — ce qu'est un artefact, les deux genres, éphémère vs durable, le modèle de visibilité, et la frontière d'isolement telle qu'elle est réellement aujourd'hui.
  • Architecture — l'enregistrement, comment la construction d'une exécution devient une cible servable, le pipeline de captures, la session de rendu lecture-seule/écrivain-unique, et la surface d'outils.

Statut (2026-08) : staging uniquement, désactivé par défaut. Toute la surface est derrière Artifacts:Enabled (désactivé par défaut) et la session de rendu interactive derrière Features:OperatorBrowseInputLock (désactivé par défaut). La mise en garde d'isolement ci-dessous est déterminante — lisez-la avant d'activer quoi que ce soit.


1. Guide de l'opérateur

1.1 Ce qu'est un artefact

ConceptSignification
ArtefactUn enregistrement durable (stringId, 8 caractères) d'une chose visualisable qu'une exécution a produite, cadré sur (run, node) — un artefact par nœud produit.
GenreWebApp (une page servie, pilotée par le pool navigateur) ou AndroidApp (un APK, rendu par le pool émulateur). Modélisé pour qu'un troisième genre n'ajoute aucun changement de schéma.
CapturesLe côté durable : un ensemble ordonné d'images PNG plus un CaptureSetHash stable, et assez de provenance de build pour réengendrer. C'est ce qui rend un artefact réouvrable et partageable.
SessionLe côté éphémère : une paire de pods en cours d'exécution (l'app servie + le relais de pixels) avec un TTL, engendrée à la demande depuis la build de l'exécution et récupérée à son expiration.
VisibilitéPrivate (l'admin du locataire producteur seulement, la valeur par défaut à la naissance) · Tenant (tout membre autorisé) · Link (un jeton de lien non authentifié — captures uniquement, jamais une session live).
État de sessionUn état distinct, rendu séparément : pas de session, live, session expirée, échec d'engendrement, build indisponible, capacité dépassée — jamais une image vide.

1.2 Éphémère vs durable

Une session de rendu live ne peut pas être conservée indéfiniment ; l'identité stable doit donc lui survivre. Rouvrir un artefact après la mort de sa session affiche ses captures, pas une erreur, et propose de réengendrer si la build est toujours disponible. Un visualiseur par lien ne voit que des captures — jamais une session capable d'entrée.

1.3 La session interactive

L'opérateur autorisé s'attache interactif par défaut — vrai clavier et souris dans l'application en cours d'exécution. Les visualiseurs supplémentaires de la même session s'attachent en lecture seule, et un attachement lecture-seule a l'air lecture-seule : leur entrée est abandonnée au relais tandis que les pixels continuent de circuler. L'entrée est à écrivain unique — exactement un visualiseur attaché détient le verrou d'entrée à la fois ; le transmettre est explicite, et deux personnes ne pilotent jamais la même session simultanément. Le navigateur dans le pod est épinglé à l'origine de l'app servie : une navigation hors de cette origine est bloquée et montrée à l'opérateur comme un refus, jamais suivie silencieusement.

1.4 Rendre un artefact accessible par un lien est une écriture

Produire un artefact est un effet de bord d'exécution et ne nécessite aucune nouvelle approbation. Activer la visibilité Link est un franchissement de frontière : c'est classé écriture, protégé, et enregistré (qui l'a activé et quand). Une identité automatisée est refusée — exactement comme decision_respond en refuse une sur un changement de gouvernance ; seul un humain authentifié peut ouvrir un lien.

1.5 La frontière d'isolement — à lire

À l'intérieur d'un locataire, un artefact de prévisualisation (code écrit par le modèle) et les environnements de test client de ce locataire partagent la frontière de rendu. C'est délibéré : un locataire est le domaine de données d'un seul client.

La frontière par locataire n'est PAS appliquée au niveau réseau sur le substrat actuel. Le pool de rendu est une instance unique partagée par cluster, et les deux clusters exécutent un CNI (Flannel) qui ne livre aucun moteur de NetworkPolicy — les politiques réseau qui existent sont donc inertes. Mesuré depuis l'intérieur du pod de rendu, une build non fiable peut atteindre l'API core de Genesis, Postgres, Keycloak, le miroir git, le point de terminaison MCP et les services d'un autre locataire ; l'authentification au niveau applicatif (jetons MCP/API, identifiants BD) tient encore, mais le réseau non. L'épinglage d'origine empêche un clic d'opérateur de devenir une requête hors-origine ; il n'empêche pas les propres requêtes de chargement de l'app servie d'atteindre des services internes.

C'est pourquoi la fonctionnalité est staging uniquement et désactivée par défaut. Elle y est bornée par cinq propriétés — pas de contenu (un clone structurel, aucun dépôt client / donnée locataire réelle / identité réelle), un opérateur unique, drapeau désactivé, aucun chemin d'identifiant vers la prod, et aucun chemin réseau privé vers la prod. Le préalable à tout usage prod ou multi-locataire est un CNI capable de politiques (Cilium/Calico) plus un durcissement du pool (ports noVNC authentifiés, browser_run_spec/eval protégés, aucun jeton de compte de service monté, espaces de noms de rendu par locataire). Cet écart est enregistré comme un écart de conformité, non porté silencieusement par le drapeau de fonctionnalité.

Si l'app testée peut atteindre quoi que ce soit d'avec état, les clics d'un opérateur provoquent de vraies écritures — visibles avant de cliquer, pas découvertes après.


2. Architecture

2.1 L'enregistrement

RunArtifact (BD locataire, run_artifacts) porte : le StringId de l'artefact ; la provenance MissionId / RunId / TaskDefId / NodeId ; Kind / Visibility / SessionState (persistés en chaîne pour qu'un troisième genre ne nécessite aucune migration) ; les champs de lien (LinkToken, expiration, activé-par/le) ; la référence de build (BuildRef, branche, sha, slug locataire, id d'environnement — assez pour réengendrer) ; l'ensemble de captures (CaptureSetHash + un CaptureManifestJson ordonné) ; et le handle de session courant quand une est live. L'unicité est (RunId, NodeId) NULLS NOT DISTINCT — un artefact par nœud produit. EnvironmentId est une simple chaîne, pas une clé étrangère : l'enregistrement d'environnement vit dans la base du plan de contrôle.

L'enregistrement est le producteur manquant d'un consommateur déjà livré en échec-fermé : RunTargetProvisioner.ResolveRunArtifactRef fait désormais une vraie recherche de la build qu'une exécution a produite au lieu d'en dériver une depuis un gabarit opérateur.

2.2 De la build à la cible servie

Il n'y a aucune construction d'image dans le core Genesis — le chemin est git-clone plus un environnement rendu par ArgoCD. Une WebApp est servie par un mode serve du chart run-built-target : la branche d'exécution est clonée dans un pod éphémère qui construit et sert l'app sur un port ; BuildRef est cette URL de service intra-cluster. Le pool navigateur loue une session, navigue vers elle, et diffuse Chrome à tête sur noVNC — le même chemin de relais que le genre émulateur/APK utilise. Le pod serve garde une posture durcie (aucun jeton de compte de service monté, non-root, seccomp, toutes capacités abandonnées).

2.3 Captures

Les captures sont des pixels, pas du code. Une image est persistée via le service de pièces jointes (base64 dans Postgres — il n'y a pas encore de stockage objet, donc un « screencast » est une séquence bornée d'images, pas de la vidéo), ajoutée au manifeste ordonné, et empreintée par un SHA-256 stable sur les ids de pièces jointes ordonnés. Elles sont servies via un point de terminaison dédié qui force image/png — du HTML/JS capturé n'est jamais servi comme document actif depuis une origine de confiance.

2.4 La session de rendu

Le relais de pixels est le proxy VNC d'operator-browse. Pour un visualiseur lecture-seule, un filtre de cadrage de messages RFB à état abandonne les messages d'entrée entiers (clavier/pointeur/presse-papier) tout en transmettant les messages d'affichage, et échoue-fermé sur tout ce qu'il ne peut pas cadrer. Qui peut écrire est un verrou d'entrée autoritatif côté core diffusé vers le relais ; le relais met en cache le détenteur localement et se traite comme lecture-seule si le flux de verrou tombe ou devient périmé (> 2 s). L'application est protégée par Features:OperatorBrowseInputLock (désactivé par défaut — le proxy verbatim hérité quand désactivé).

2.5 Surface d'outils

MCP en lecture seule : artifact_list (par mission ou exécution), artifact_get (métadonnées + URLs de captures), et artifact_show (émettre le bloc de rendu dans la conversation pour que la carte s'affiche en ligne). Tous sont tenant-admin, protégés par Artifacts:Enabled, et nés attribués. La publication de lien n'est délibérément pas un outil MCP — c'est une action humaine derrière le point de passage de décision.

2.6 Ce qui n'est pas implémenté

Servir le HTML d'un artefact au navigateur propre d'un humain (un modèle de sécurité d'isolement d'origine différent) ; l'édition, le versionnage ou la comparaison d'artefacts ; et les screencasts vidéo (reportés jusqu'à l'existence d'un stockage objet). La frontière réseau par locataire (voir §1.5) est le préalable déterminant pour la prod.