# Delta `0.2.14-pre.004` — decoder instruction-only + implémentation externe ## 1. Base requise Cette tranche s'applique exclusivement sur : ```text v0.2.13 + 0.2.14-pre.001 + 0.2.14-pre.002 + 0.2.14-pre.003 ``` Le gate opérateur fourni pour `pre.003` est intégralement vert : ```text cargo fmt --all PASS python3 scripts/audit_rust_workspace_rules.py PASS python3 scripts/audit_markdown_tables.py ... PASS — 171 tables / 120 fichiers cargo check --workspace PASS cargo clippy --workspace --all-targets PASS cargo test -p ksp-program-api PASS — 8 tests Rust cargo test --workspace PASS cargo tree -p ksp-program-api --edges normal PASS — Core + Interface uniquement cargo tree --duplicates exécuté ``` La version workspace passe de : ```text 0.2.14-pre.3 ``` à : ```text 0.2.14-pre.4 ``` Commit attendu : ```text v0.2.14-pre.004 ``` ## 2. Objectif Matérialiser le contrat instruction-only ouvert retenu par `pre.001` : ```text ProgramInstructionDecoder: Send + Sync associated type Decoded program_ids(&self) -> &[Pubkey] recognize(&self, &ProgramInstruction) -> ProgramInstructionRecognition decode(&self, &ProgramInstruction) -> Result> ``` Puis prouver qu'une crate consommatrice séparée peut l'implémenter avec son propre type décodé et un Program `Pubkey` absent du registry Core. ## 3. Trait `ProgramInstructionDecoder` Le trait est défini dans un module privé et réexporté depuis le crate-root. Propriétés : ```text Send + Sync requis sur l'implémentation Decoded associated type sans bound imposé program_ids slice de Pubkey opaques recognize sélection instruction-local explicite decode Result Core + outcome générique méthodes par défaut aucune registry / object composition aucune promesse ``` `decode` est destiné à une instruction déjà sélectionnée via `program_ids` / `recognize`; il ne remplace pas le signal de reconnaissance. ## 4. Open-world Program IDs Aucun `ProgramKind`, enum centrale ou validation contre le registry Core n'est introduit. Le canari externe utilise : ```text Pubkey::new_from_array([0xE7; 32]) ``` et vérifie explicitement : ```text ksp_core_lib::find_program_pubkey(&external_program_id) == None ``` Cette consultation du registry est limitée au test négatif. `ksp-program-api` ne réexporte pas `find_program_pubkey` et le decoder externe n'a besoin que de la façade Program API pour son implémentation. ## 5. Associated output externe Le test d'intégration définit hors du code de production : ```text ExternalDecodedInstruction ExternalProgramDecoder ``` `ExternalProgramDecoder` implémente le trait et produit : ```text ExactMatch pour l'opcode supporté ProgramMatch pour le Program connu avec opcode non supporté NoMatch pour un autre Program Decoded(...) pour l'opcode supporté Unsupported pour le Program connu mais non supporté ``` Aucun `Any`, JSON, serde, codec ou enum centrale n'intervient dans le transport du type décodé. ## 6. Dependency firewall Le manifest de `ksp-program-api` reste inchangé : ```text ksp-core-lib ksp-interface-lib ``` Toujours absents : ```text ksp-program-lib ksp-logging-lib Transport / Config / Wallet / Store / Materializer serde / serde_json borsh / bincode / wincode solana-instruction reqwest / tokio / tonic / tauri / tracing ``` ## 7. Tests ### Public API `tests/public_api.rs` prouve que le trait est implémentable depuis la façade crate-root sans module privé. ### External implementation `tests/external_implementation.rs` est compilé par Cargo comme crate d'intégration séparée et vérifie : ```text Send + Sync de l'implémentation associated output tiers Program Pubkey non enregistré program_ids NoMatch / ProgramMatch / ExactMatch Decoded / Unsupported absence de ksp-program-lib ``` ### Boundary `tests/dependency_boundary.rs` avance l'inventaire autorisé jusqu'au trait et maintient l'absence de preparer, logging, serde, `Any` et module public. ## 8. Fichiers ajoutés ```text crates/ksp-program-api/src/program_instruction_decoder.rs crates/ksp-program-api/tests/external_implementation.rs deltas/0.2.14/pre.004.md ``` ## 9. Fichiers modifiés ```text Cargo.toml crates/ksp-program-api/README.md crates/ksp-program-api/USAGE.md crates/ksp-program-api/src/lib.rs crates/ksp-program-api/tests/dependency_boundary.rs crates/ksp-program-api/tests/public_api.rs docs/plans/021-V0_2_14_PROGRAM_API_PLAN.md docs/validation/017-V0_2_14_PROGRAM_API.md ``` ## 10. Fichiers volontairement inchangés ```text crates/ksp-program-api/Cargo.toml README.md RULES.md ROADMAP.md CHANGELOG.md docs/architecture/** docs/rules/** crates/ksp-core-lib/** crates/ksp-interface-lib/** crates/ksp-logging-lib/** crates/ksp-*-transport-lib/** crates/ksp-config-lib/** crates/ksp-wallet-lib/** crates/ksp-app-*/** prompts/** ``` ## 11. Scope négatif maintenu Cette tranche n'introduit pas : ```text runtime decoder registry Vec> object-safety promise identity/version/coverage descriptor priority/conflict policy ProgramAccountDecoder / Event / ReturnData payload canonique D3 serde / JSON / Any proof/confidence contextuels ProgramExecutionPreparer ExecutionPolicy / Execution ``` ## 12. Gate opérateur attendu Après application : ```bash cargo fmt --all python3 scripts/audit_rust_workspace_rules.py python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.14 cargo check --workspace cargo clippy --workspace --all-targets cargo test -p ksp-program-api cargo test --workspace cargo tree -p ksp-program-api --edges normal cargo tree --duplicates ``` Le graphe normal doit rester : ```text ksp-program-api ├── ksp-core-lib └── ksp-interface-lib └── ksp-core-lib ``` Une correction découverte par ce gate reste un `0.2.14-pre.004-fix.NNN` et n'avance pas `pre.005`. ## 13. Suite Après gate vert, `pre.005` doit rester une tranche de hardening/API completeness : ```text bounds et sécurité Debug/error inventaire exact des exports publics absence de closed-world Program enum absence de serde/codec/logging/runtime scope négatif registry/preparer/payload D3 test de complétude release ``` Aucun nouveau contrat fonctionnel n'est prévu dans `pre.005`.