# Utilisation de ks-pipeline-demo-scenarios ## Objectif La bibliothèque expose des scénarios Devnet contrôlés. Le binaire permet leur exécution hors de l’application desktop lorsque la commande correspondante est disponible. ## Wallet sélectionné par le profil `inspect_selected_profile_wallet()` résout l'alias persistant non sensible configuré dans `ks-config`. `unlock_selected_profile_wallet()` exige ensuite un `ks_wallet::WalletPassword` fourni explicitement par le consommateur. Les scénarios historiques qui utilisent encore le wallet temporaire refusent un fallback silencieux lorsqu'un `wallet_alias` persistant est configuré. Le CLI `prepare-token-2022-fixture` reste un workflow externe Solana CLI : son `--wallet` doit viser un keypair JSON exporté explicitement et refuse un fichier `.kswallet`. ## Initialiser l’environnement ```rust fn initialize_environment() -> ks_core::Result<()> { ks_pipeline_demo_scenarios::initialize_demo_scenario_environment() } ``` `resolve_demo_devnet_profile` sélectionne un profil Devnet configuré et `prepare_demo_devnet_profile_store` vérifie ou initialise son stockage PostgreSQL. ## Observer une exécution `NoopSolanaExecutionObserver` convient aux appels sans progression personnalisée. ```rust fn observer() -> ks_pipeline_demo_scenarios::NoopSolanaExecutionObserver { ks_pipeline_demo_scenarios::NoopSolanaExecutionObserver } ``` Une application peut implémenter `SolanaExecutionObserver` pour recevoir les événements et demander une annulation coopérative. ## Préparer une simulation System Program ```rust fn system_transfer_request( recipient: ks_lib::MdPubkey, ) -> ks_pipeline_demo_scenarios::DevnetSystemTransferRequest { ks_pipeline_demo_scenarios::DevnetSystemTransferRequest::new( "system-transfer-001", recipient, 1_000, ) } ``` Le constructeur crée une requête conservative : `submit` et `operator_confirmed` restent à `false`. ## Préparer et exécuter un Memo v4 ```rust fn memo_request() -> ks_pipeline_demo_scenarios::DevnetMemoExecutionRequest { ks_pipeline_demo_scenarios::DevnetMemoExecutionRequest::new( "memo-001", "khadhroony-bot3 Devnet validation", ) } ``` ```rust async fn run_memo( store: &S, observer: &O, request: ks_pipeline_demo_scenarios::DevnetMemoExecutionRequest, ) -> ks_core::Result where S: ks_store::Store + Sync, O: ks_pipeline_demo_scenarios::SolanaExecutionObserver, { let result = ks_pipeline_demo_scenarios::execute_devnet_memo( store, observer, request, ) .await; match result { Ok(summary) => Ok(summary), Err(error) => Err(error), } } ``` ## Simuler Token-2022 ```rust async fn simulate_token_2022( request: ks_pipeline_demo_scenarios::DevnetSplToken2022ExecutionRequest, observer: &O, ) -> ks_core::Result where O: ks_pipeline_demo_scenarios::SolanaExecutionObserver, { let result = ks_pipeline_demo_scenarios::simulate_devnet_spl_token_2022(request, observer).await; match result { Ok(summary) => Ok(summary), Err(error) => Err(error), } } ``` ## Exécuter la campagne Devnet Token-2022 Token Metadata La campagne `0.4.8-pre.012` prépare un mint Token-2022 frais, initialise son `MetadataPointer` vers le mint lui-même, puis exécute les cinq opérations Token Metadata dans l’ordre. Elle soumet des transactions réelles et exige donc une confirmation opérateur explicite. Le test opt-in est le chemin opérateur recommandé : ```bash KS_DEVNET_TOKEN_2022_METADATA_CAMPAIGN_TEST=1 \ KS_DEVNET_TOKEN_2022_METADATA_OPERATOR_CONFIRMED=1 \ KS_SECRET_POSTGRES_TEST_URL='postgresql://…' \ cargo test -p ks-pipeline-demo-scenarios optional_devnet_token_2022_metadata_campaign_from_env -- --nocapture ``` Par défaut, les scénarios utilisent directement les `default_profile` des documents partagés via `ks_config::read_default_shared_app_config_with_environment`. `KS_DEVNET_PROFILE` peut sélectionner explicitement un profil Devnet compatible. `KS_DEVNET_CONFIG_PATH` reste un override facultatif vers une composition explicite ; aucun fichier de composition propre aux scénarios n’est requis. Le profil runtime doit utiliser un wallet persistant, autoriser les soumissions Devnet via la politique execution et respecter les plafonds de dépense et de frais. La sortie `TOKEN_2022_METADATA_FIXTURE` conserve le mint et la signature de préparation. Chaque ligne `TOKEN_2022_METADATA_STEP` conserve l’opération, la signature, le slot, la postcondition, le nombre de matérialisations et, pour `Emit`, la taille du `returnData`. La campagne de référence de `pre.012` a été exécutée avec succès et ses preuves sont conservées dans `SPL_TOKEN_2022_METADATA_DEVNET_VALIDATION_MATRIX.json` et dans le rapport `docs/validation/V0_4_8_METADATA_VALIDATION_REPORT.md` ; le rapport détaillé `pre.012` est archivé sous `olddocs/archivekbot3/docs/validation/`. Une nouvelle exécution produit de nouvelles transactions et ne remplace ces preuves qu’après validation explicite. ## Charger la matrice de validation Token-2022 ```rust fn load_validation_matrix( ) -> ks_core::Result { let result = ks_pipeline_demo_scenarios::load_token_2022_validation_matrix(); match result { Ok(matrix) => Ok(matrix), Err(error) => Err(error), } } ``` La matrice provient de `test-fixtures/contract-matrices/SPL_TOKEN_2022_VALIDATION_MATRIX.json` et est validée avant d’être retournée. ## Parcourir les scénarios publics ```rust fn scenario_ids() -> Vec { ks_pipeline_demo_scenarios::devnet_spl_validation_scenarios() .iter() .map(|scenario| scenario.id.clone()) .collect() } ``` Cette liste permet au CLI ou au desktop de présenter l’inventaire sans dupliquer les identifiants. ## Préparer une fixture Token-2022 ```rust async fn prepare_fixture( wallet_path: std::path::PathBuf, wallet_dir: std::path::PathBuf, ) -> ks_core::Result { let options = ks_pipeline_demo_scenarios::Token2022FixturePreparationOptions { rpc_url: "https://api.devnet.solana.com".to_string(), wallet_path, wallet_dir, decimals: 9, }; let result = ks_pipeline_demo_scenarios::prepare_token_2022_fixture(&options).await; match result { Ok(summary) => Ok(summary), Err(error) => Err(error), } } ``` La préparation peut créer des comptes et exécuter des commandes Solana CLI. Elle doit rester explicitement déclenchée par l’opérateur. ## Tests de référence Les tests de cette crate sont particulièrement utiles pour comprendre l’orchestration réelle : - validation de la matrice Token-2022 ; - préparation et réutilisation des fixtures ; - simulation et exécution des scénarios ; - hydratation canonique, extraction Core, replay et matérialisation ; - idempotence et confirmation des états finaux ; - test du binaire `ks-pipeline-demo-scenarios-cli`. Les tests Devnet restent opt-in et peuvent produire des transactions réelles lorsque la soumission est explicitement activée. ## Erreurs et invariants - aucun envoi ne doit résulter d’une simple demande de simulation ; - le profil doit cibler Devnet pour les scénarios Devnet ; - les secrets restent gérés par `ks-wallet` ; - les résumés ne doivent pas déclarer une validation non observée ; - ElGamal ne doit pas être présenté comme validé sur réseau. ## Limites durables - cette crate est destinée aux démonstrations, validations et outils opérateur, pas au moteur de production autonome ; - les scénarios réseau exigent un endpoint, un wallet et des fonds compatibles ; - la bibliothèque ne fournit pas d’interface graphique. ## Charger les scénarios synthétiques Metaplex ```rust fn metaplex_scenario_ids() -> Vec { ks_pipeline_demo_scenarios::metaplex_token_metadata_synthetic_scenarios() .iter() .map(|scenario| scenario.id.clone()) .collect() } ``` L’inventaire couvre NFT, SFT, token fongible, collection et pNFT. Ces scénarios sont déterministes et ne soumettent aucune transaction. ## Charger la matrice de validation Metaplex ```rust fn load_metaplex_matrix( ) -> ks_core::Result { let result = ks_pipeline_demo_scenarios::load_metaplex_token_metadata_validation_matrix(); match result { Ok(matrix) => Ok(matrix), Err(error) => Err(error), } } ``` Une validation réseau ne peut passer à `confirmed` que lorsque toutes les preuves déclarées sont présentes. Les statuts `not_run` et `unavailable` n’acceptent aucune preuve observée. ## Inventaire Metaplex Devnet ```rust let scenarios = ks_pipeline_demo_scenarios::metaplex_token_metadata_devnet_scenarios(); assert!(scenarios.iter().all(|scenario| { scenario.mode == ks_pipeline_demo_scenarios::MetaplexTokenMetadataScenarioMode::NetworkSimulation })); ``` Cet inventaire prépare les tests et campagnes Devnet simulation-first. Il ne constitue pas à lui seul une validation réseau réussie. ## Charger le corpus de validation croisée Metaplex ```rust fn load_metaplex_cross_validation() -> ks_core::Result { let matrix = ks_pipeline_demo_scenarios::load_metaplex_token_metadata_cross_validation_matrix(); return match matrix { Ok(value) => Ok(value.cases.len()), Err(error) => Err(error), }; } ``` Les cas marqués `requiresNetworkEvidence` ne peuvent pas être déclarés validés à partir de preuves synthétiques. Une simulation, une soumission ou une confirmation exige les preuves RPC déclarées par le cas. ## Simuler une opération Metaplex réelle sur Devnet L’API réseau prend un intent Metaplex typé déjà lié aux comptes et autorités de la fixture. Le wallet persistant du profil doit être l’unique signer requis. ```rust async fn simulate_metaplex_operation( pool: &ks_onchain_transport::HttpEndpointPool, profile: &ks_config::ProfileConfig, workspace_root: &std::path::Path, operation: ks_lib::ExMetaplexTokenMetadataOperation, observer: &O, ) -> ks_core::Result where O: ks_pipeline_demo_scenarios::SolanaExecutionObserver, { let request = ks_pipeline_demo_scenarios::DevnetMetaplexTokenMetadataExecutionRequest::new( "metaplex-devnet-simulation", operation, ); return ks_pipeline_demo_scenarios::simulate_devnet_metaplex_token_metadata( pool, profile, workspace_root, &request, observer, ) .await; } ``` Pour une soumission, l’appelant doit définir `submit = true` et `operator_confirmed = true`. Les opérations dépréciées sont refusées par cette API automatique. Les lectures `preflight_reads` et `postcondition_reads` doivent décrire les comptes Metadata, Edition, Token Record ou Collection nécessaires au scénario. ## Charger la matrice des opérations courantes ```rust fn load_metaplex_devnet_contract( ) -> ks_core::Result { return ks_pipeline_demo_scenarios::load_metaplex_token_metadata_devnet_execution_matrix(); } ``` La matrice ne transforme jamais une implémentation disponible en validation réseau. Les valeurs restent `not_run` jusqu’à l’observation d’une simulation ou d’une confirmation réelle. ## Exécuter une opération Metaplex courante depuis un JSON typé Le runner Devnet commun accepte toute variante non dépréciée de `ExMetaplexTokenMetadataOperation`. Le test opt-in peut être lancé avec : ```bash KS_DEVNET_METAPLEX_EXECUTION_TEST=1 \ KS_DEVNET_METAPLEX_OPERATION_JSON='{"operation":"..."}' \ cargo test -p ks-pipeline-demo-scenarios optional_devnet_current_operation_from_env -- --nocapture ``` Variables optionnelles : - `KS_DEVNET_WALLET_DIR` pour sélectionner le répertoire du wallet persistant ; - `KS_SECRET_POSTGRES_TEST_URL` est obligatoire pour le test opt-in afin de permettre l’hydratation canonique, l’extraction Core, le replay et la vérification d’idempotence après une soumission ; - `KS_DEVNET_METAPLEX_PREFLIGHT_READS_JSON` pour fournir les lectures stateful avant simulation ; - `KS_DEVNET_METAPLEX_POSTCONDITION_READS_JSON` pour fournir les lectures après confirmation ; - `KS_DEVNET_METAPLEX_MATERIALIZE_AFTER_CONFIRMATION=1` pour rendre obligatoire la preuve de matérialisation et produire les snapshots stateful demandés ; - `KS_DEVNET_METAPLEX_SUBMIT=1` pour autoriser la soumission ; - `KS_DEVNET_METAPLEX_OPERATOR_CONFIRMED=1` pour confirmer explicitement la soumission. La soumission reste impossible si `KS_DEVNET_METAPLEX_SUBMIT` et `KS_DEVNET_METAPLEX_OPERATOR_CONFIRMED` ne sont pas activées ensemble. Après confirmation, le runner hydrate la transaction canonique, exécute l’extraction Core, rejoue le décodeur Metaplex avec ses matérialiseurs puis effectue une seconde passe qui doit être idempotente. Les opérations dépréciées sont rejetées avant tout appel RPC. ## Préparer une campagne Metaplex courante Les exemples contenant `...` ne sont jamais exécutables. Pour connaître les opérations acceptées : ```rust let operations = ks_pipeline_demo_scenarios::metaplex_token_metadata_current_operation_names(); ``` Les opérations disposant d’un modèle générique peuvent être inspectées ainsi : ```rust fn verify_template() -> ks_core::Result { ks_pipeline_demo_scenarios::metaplex_token_metadata_current_operation_json_template( "verify", ) } ``` Les valeurs entre chevrons doivent être remplacées par les comptes Devnet réels. Le modèle peut ensuite être passé à `DevnetMetaplexTokenMetadataExecutionRequest::from_operation_json`. Pour les parcours créateur, il est préférable de ne pas écrire de JSON manuellement : ```rust fn creator_verify_request( authority: ks_lib::MdPubkey, metadata: ks_lib::MdPubkey, ) -> ks_pipeline_demo_scenarios::DevnetMetaplexTokenMetadataExecutionRequest { ks_pipeline_demo_scenarios::devnet_metaplex_creator_verify_request( "metaplex-verify-creator", authority, metadata, ) } ``` `devnet_metaplex_creator_unverify_request` fournit le parcours inverse. Les deux requêtes restent en simulation-only par défaut. Le test générique accepte toujours `KS_DEVNET_METAPLEX_OPERATION_JSON`, mais rejette explicitement le placeholder documentaire `...`. Les opérations plus complexes recevront des préparateurs de fixtures dédiés au lieu d’exiger de longs JSON écrits à la main. ## Préparer `Create` puis `Mint` avec un ATA canonique `prepare_metaplex_create_fixture` prépare maintenant une base réseau commune aux cinq familles : un mint SPL classique frais et l’ATA canonique de l’opérateur sont créés dans une même transaction native, puis relus et validés. La supply du mint et le solde de l’ATA doivent rester à zéro à cette étape afin que l’opération Metaplex `Mint` constitue la preuve de création de supply. Le résumé expose notamment : - `token_account`, l’ATA classique canonique ; - `token_record`, renseigné uniquement pour le pNFT ; - `mint_amount_raw`, égal à `1` pour NFT/collection/pNFT, `10` pour SFT et `1_000_000_000` pour le fungible à 9 décimales ; - `operation_json`, l’intent `Create` typé ; - `mint_operation_json`, l’intent `Mint` typé à exécuter seulement après confirmation de `Create`. Le token record pNFT est uniquement dérivé pendant la préparation. Sa création reste la responsabilité de `Mint`, de sorte que la fixture ne fabrique pas à l’avance l’état que la campagne doit démontrer. ## Exécuter la campagne Devnet `Create -> Mint` La campagne est volontairement exécutée **une famille à la fois**. Elle prépare une fixture fraîche, soumet `Create`, vérifie que la supply et l’ATA restent à zéro, puis soumet `Mint` et exige la transition exacte vers `mint_amount_raw`. Pour NFT, collection et pNFT, la validation SPL exige après `Create` que les mint/freeze authorities aient été transférées au PDA Master Edition ; pour SFT et fungible, elles restent sur l’opérateur. Après `Mint`, NFT/SFT/fungible/collection exigent un ATA `Initialized` (`state=1`), alors que pNFT exige un ATA `Frozen` (`state=2`) et un Token Record Metaplex présent. Toutes les lectures Metaplex postcondition sont automatiquement bornées au slot de confirmation. Le test opt-in utilise `nft` par défaut. Les valeurs acceptées par `KS_DEVNET_METAPLEX_CREATE_MINT_FAMILY` sont `nft`, `sft`, `fungible`, `collection`, `programmable_nft` et l’alias `pnft`. ```bash KS_DEVNET_METAPLEX_CREATE_MINT_CAMPAIGN_TEST=1 \ KS_DEVNET_METAPLEX_OPERATOR_CONFIRMED=1 \ KS_DEVNET_METAPLEX_CREATE_MINT_FAMILY=nft \ KS_SECRET_POSTGRES_TEST_URL='postgresql://…' \ cargo test -p ks-pipeline-demo-scenarios \ optional_devnet_metaplex_create_mint_campaign_from_env \ -- --nocapture ``` La sortie `METAPLEX_CREATE_MINT_FIXTURE` conserve les comptes de la fixture. Deux lignes `METAPLEX_CREATE_MINT_STEP` exposent ensuite les signatures/slots de `Create` et `Mint`, les matérialisations, la transition supply/amount et les états bruts de l’ATA avant/après `Mint`. Ces valeurs doivent être conservées avant de promouvoir une ligne de la matrice réseau. ## Exécuter la campagne collection `Verify -> Unverify` `execute_devnet_metaplex_collection_verify_campaign` réutilise les fixtures `Create -> Mint` qualifiées pour construire un Collection NFT parent et un NFT membre lié au parent avec `verified=false`. Le runner exécute ensuite les wrappers courants `Verify(CollectionV1)` puis `Unverify(CollectionV1)`. La preuve stateful exige simultanément : - `collection.verified` du membre : `false -> true -> false` ; - `CollectionDetails::V1.size` du parent : `0 -> 1 -> 0` ; - Metadata PDA membre et parent présentes ; - Master Edition parent présente ; - hydratation canonique, Core extraction, replay, matérialisation et idempotence propres pour `Verify` et `Unverify`. Le parcours ne soumet pas `SetCollectionSize` : cette opération est obsolète et ne doit pas fabriquer artificiellement la postcondition de taille. ```bash KS_DEVNET_METAPLEX_COLLECTION_VERIFY_CAMPAIGN_TEST=1 \ KS_DEVNET_METAPLEX_OPERATOR_CONFIRMED=1 \ KS_SECRET_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ cargo test -p ks-pipeline-demo-scenarios \ optional_devnet_metaplex_collection_verify_campaign_from_env \ -- --nocapture ``` Une exécution réussie imprime `METAPLEX_COLLECTION_VERIFY_FIXTURE`, deux lignes `METAPLEX_COLLECTION_VERIFY_STEP`, l’état fermé `METAPLEX_COLLECTION_VERIFY_STATE` et une ligne machine-readable `METAPLEX_COLLECTION_VERIFY_EVIDENCE`. La campagne qualifiante a été conservée sous `pre.013` ; `Verify` et `Unverify` sont désormais `confirmed` sur la famille `collection` dans la matrice Devnet. ## Exécuter la campagne pNFT `Delegate -> Lock -> Unlock -> Revoke -> Delegate -> Transfer` `execute_devnet_metaplex_pnft_lifecycle_campaign` crée d’abord une fixture `programmable_nft` fraîche via la campagne `Create -> Mint` qualifiée. Il crée ensuite un wallet temporaire de delegate et un propriétaire destination frais, puis enchaîne deux rôles de Token Delegate distincts : 1. `Delegate(StakingV1)` par le propriétaire ; 2. `Lock` puis `Unlock` par le staking delegate ; 3. `Revoke(StakingV1)` par le propriétaire ; 4. `Delegate(TransferV1)` par le propriétaire ; 5. `Transfer` par le transfer delegate vers une ATA destination fraîche. Le runner exige que le Token Record source passe `Unlocked/Staking -> Locked/Staking -> Unlocked/Staking -> delegate cleared -> Unlocked/Transfer`. Après le transfert, l’ATA source doit contenir `0` et rester `Initialized`, tandis que l’ATA destination doit contenir `1` et rester `Frozen`; le Token Record destination doit être `Unlocked` sans delegate. Ce contrat suit le handler courant, qui thaw la source avant transfert puis freeze uniquement la destination. Les wallets temporaires ne remplacent jamais le wallet persistant du profil comme fee payer ; seul le delegate explicitement déclaré est ajouté aux signataires des étapes qui l’exigent. ```bash KS_DEVNET_METAPLEX_PNFT_LIFECYCLE_CAMPAIGN_TEST=1 \ KS_DEVNET_METAPLEX_OPERATOR_CONFIRMED=1 \ KS_SECRET_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ cargo test -p ks-pipeline-demo-scenarios \ optional_devnet_metaplex_pnft_lifecycle_campaign_from_env \ -- --nocapture ``` Une exécution qualifiante doit terminer `ok` et produire `METAPLEX_PNFT_LIFECYCLE_FIXTURE`, six lignes `METAPLEX_PNFT_LIFECYCLE_STEP`, `METAPLEX_PNFT_LIFECYCLE_STATE` et `METAPLEX_PNFT_LIFECYCLE_EVIDENCE`. Le bundle qualifiant a été observé sous `pre.013` ; `Delegate`, `Revoke`, `Lock`, `Unlock` et `Transfer` sont désormais `confirmed` sur `programmable_nft`. ## Campagne Devnet Metaplex Token Owned Escrow La campagne escrow prépare un NFT classique parent et un token fungible attribut, puis exécute un cycle `TokenOwner` strictement contrôlé. Elle crée le PDA escrow, crée son ATA SPL classique pour le mint attribut, y dépose exactement une unité brute, transfère cette unité vers l’ATA de l’opérateur puis ferme le compte escrow. ```bash KS_DEVNET_METAPLEX_ESCROW_CAMPAIGN_TEST=1 \ KS_DEVNET_METAPLEX_OPERATOR_CONFIRMED=1 \ KS_SECRET_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ cargo test -p ks-pipeline-demo-scenarios \ optional_devnet_metaplex_escrow_campaign_from_env \ -- --nocapture ``` Une exécution qualifiante doit produire `METAPLEX_ESCROW_FIXTURE`, deux lignes `METAPLEX_ESCROW_SETUP`, trois lignes `METAPLEX_ESCROW_STEP`, `METAPLEX_ESCROW_STATE` et `METAPLEX_ESCROW_EVIDENCE`. Le bundle complet a été observé sous `pre.013` ; `CreateEscrowAccount`, `TransferOutOfEscrow` et `CloseEscrowAccount` sont désormais `confirmed` sur la famille `nft`. ## Campagne Devnet Metaplex maintenance La campagne `maintenance_campaign` ferme les opérations courantes qui ne relèvent pas des parcours précédents. Elle crée un NFT classique frais, soumet réellement `Update` avec la transition bornée `primary_sale_happened: false -> true`, puis traite les surfaces non qualifiables positivement avec des probes **simulation-only**. Une simulation négative n'autorise jamais une soumission. ```bash KS_DEVNET_METAPLEX_MAINTENANCE_CAMPAIGN_TEST=1 \ KS_DEVNET_METAPLEX_OPERATOR_CONFIRMED=1 \ KS_SECRET_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ cargo test -p ks-pipeline-demo-scenarios \ optional_devnet_metaplex_maintenance_campaign_from_env \ -- --nocapture ``` Le bundle qualifiant de `pre.013` confirme `Update` et conserve les refus runtime exacts suivants : - `Resize` : `Custom(201)`, compte déjà au layout cible ; - `Migrate` : `Custom(75)`, instruction retirée ; - `Collect` : `Custom(7)`, autorité de collecte Metaplex réservée ; - `CloseAccounts` : `Custom(188)`, autorité ownerless-close Metaplex réservée. Le bilan courant de la matrice Devnet Metaplex est **15 `confirmed`, 5 `unavailable`, 0 `not_run`**. Les campagnes restent réexécutables pour diagnostiquer une régression ou un changement de runtime, mais ne constituent plus des tâches ouvertes de `pre.013`. ### Preuve machine-readable `Create -> Mint` À partir de `pre.013-delta-fix-012`, une campagne réussie imprime en plus une ligne unique `METAPLEX_CREATE_MINT_EVIDENCE`. Elle contient les deux objets `create` et `mint` avec le cluster, le genesis hash, le slot RPC de simulation, le nombre de logs, le message hash exact, le fee estimé, la signature, le statut/slot de confirmation, les nombres de snapshots et matérialisations ainsi que les drapeaux canonical/Core/replay/idempotence. Cette ligne est la forme à conserver pour les futures promotions de matrice. Les cinq familles NFT, SFT, fungible, collection et pNFT sont confirmées. `Create` et `Mint` disposent chacune de cinq bundles `familyEvidence`; le pNFT prouve en plus la création du Token Record et l’état ATA `Initialized(1) -> Frozen(2)`. ## Probe Devnet Metaplex `Use` La surface courante `Use` est classée `unavailable` sur Devnet : la simulation exacte du discriminant 51 retourne `InvalidInstructionData` et aucune transaction n'est soumise. Le test opt-in reste utile comme contrôle de non-régression du runtime : ```bash KS_DEVNET_METAPLEX_USE_PROBE_TEST=1 \ KS_DEVNET_METAPLEX_OPERATOR_CONFIRMED=1 \ KS_SECRET_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ cargo test -p ks-pipeline-demo-scenarios \ optional_devnet_metaplex_use_probe_from_env \ -- --nocapture ``` Une simulation négative est retournée comme résultat uniquement lorsque `submit=false`. Elle ne peut jamais autoriser une signature ou une soumission. ## Solana Program Metadata L’inventaire fermé est exposé par : ```rust fn program_metadata_scenarios( ) -> Vec { ks_pipeline_demo_scenarios::solana_program_metadata_devnet_scenarios() } ``` Les deux parcours sont ordonnés ainsi : 1. `Allocate → Extend → Write → SetAuthority → Trim → Close` pour le `Buffer` non canonique ; 2. `Initialize → SetData → SetImmutable` pour le compte `Metadata` non canonique. `Extend` précède volontairement `Write` : `Allocate` ne réserve que l’en-tête du Buffer, alors que l’écriture exige une capacité suffisante. La préparation crée deux PDA non canoniques, les préfinance avec deux transferts System Program confirmés, puis produit neuf opérations typées avec leurs lectures, preuves de rent et postconditions. Elle exige un profil Devnet persistant, `devnet_send_enabled` et une confirmation opérateur explicite. ```rust async fn run_program_metadata_campaign( pool: &ks_onchain_transport::HttpEndpointPool, profile: &ks_config::ProfileConfig, workspace_root: &std::path::Path, observer: &O, ) -> ks_core::Result where O: ks_pipeline_demo_scenarios::SolanaExecutionObserver, { let options = ks_pipeline_demo_scenarios::SolanaProgramMetadataFixturePreparationOptions { query_role: "http_queries".to_string(), transaction_role: "http_transactions".to_string(), operator_confirmed: true, }; ks_pipeline_demo_scenarios::execute_devnet_solana_program_metadata_campaign( pool, profile, workspace_root, &options, observer, ) .await } ``` Cet appel crée des comptes et soumet onze transactions réelles au minimum : deux transferts de préfinancement et neuf opérations `ProgM6…`. Il ne doit jamais être déclenché implicitement par une ouverture de fenêtre ou une simple demande de simulation. La matrice `SOLANA_PROGRAM_METADATA_DEVNET_VALIDATION_MATRIX.json` est fermée à neuf opérations `confirmed` depuis `0.4.8-pre.010`. Les deux parcours conservent leurs signatures, simulations, postconditions et preuves matérialisées ; `Close` utilise une preuve `account_absence`, car un compte absent ne produit pas de snapshot matérialisé. Une réexécution n’est nécessaire qu’en cas de régression ou de changement du runtime Devnet.