v0.2.14-pre.003

This commit is contained in:
2026-08-28 14:10:49 +02:00
parent c1f61a380f
commit 27d4cb1f36
11 changed files with 588 additions and 68 deletions

View File

@@ -1,12 +1,12 @@
# file: Cargo.toml
# version: 314
# version: 315
[workspace]
resolver = "3"
members = ["crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-wallet-lib"]
[workspace.package]
version = "0.2.14-pre.2"
version = "0.2.14-pre.3"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"

View File

@@ -1,11 +1,11 @@
<!-- file: crates/ksp-program-api/README.md -->
<!-- version: 1 -->
<!-- version: 2 -->
# ksp-program-api
`ksp-program-api` est la façade publique ouverte du domaine Program KSP. Elle est destinée aux implémentations officielles futures comme aux crates Program externes et ne possède pas les implémentations concrètes.
La tranche initiale `0.2.14-pre.002` matérialise uniquement le scaffold et les types déjà possédés par les couches fondatrices. Aucun trait decoder n'est encore publié.
À partir de `0.2.14-pre.003`, la foundation expose les types Core/Interface retenus ainsi que le vocabulaire minimal de reconnaissance et d'outcome de décodage. Le trait decoder reste réservé à la tranche suivante.
## Ownership
@@ -35,11 +35,43 @@ ProgramAccountMeta
ProgramInstruction
```
`ksp-program-api` les réexporte depuis son crate-root pour offrir une façade de consommation stable sans dupliquer leurs types ni transférer leur ownership.
Program API possède désormais :
## Surface de scaffold
```text
ProgramInstructionRecognition
ProgramInstructionDecodeOutcome<Decoded>
```
La façade `pre.002` expose exactement :
`ksp-program-api` réexporte l'ensemble depuis son crate-root pour offrir une façade de consommation stable sans dupliquer les types Core/Interface ni transférer leur ownership.
## Recognition
`ProgramInstructionRecognition` est `#[non_exhaustive]` et possède trois états :
```text
NoMatch l'implémentation ne revendique pas l'instruction
ProgramMatch le Program ou la famille correspond, sans reconnaissance exacte
ExactMatch l'implémentation affirme un match instruction-local exact
```
Cette reconnaissance ne contient aucun score, priorité, proof, confidence, discriminator textuel ou inventaire central de Programs.
## Decode outcome
`ProgramInstructionDecodeOutcome<Decoded>` est également `#[non_exhaustive]` :
```text
Decoded(Decoded) valeur typée possédée par l'implémentation
Unsupported instruction connue mais non supportée par cette capability
```
Un futur `decode(...)` utilisera le `Result` Core : une erreur de validation/décodage restera donc `Err`, sans variante parallèle `Failed`.
Le `Debug` de l'outcome n'impose pas `Decoded: Debug` et n'affiche jamais la valeur `Decoded`. Il produit uniquement le nom sûr de l'état (`Decoded` ou `Unsupported`).
## Surface actuelle
La façade `pre.003` expose :
```text
Error
@@ -49,16 +81,16 @@ Result
Pubkey
ProgramAccountMeta
ProgramInstruction
ProgramInstructionRecognition
ProgramInstructionDecodeOutcome<Decoded>
```
Aucun module interne n'est public.
Les contrats suivants restent réservés aux tranches suivantes :
Le contrat suivant reste réservé à `pre.004` :
```text
ProgramInstructionRecognition pre.003
ProgramInstructionDecodeOutcome<T> pre.003
ProgramInstructionDecoder pre.004
ProgramInstructionDecoder
```
## Frontières

View File

@@ -1,9 +1,9 @@
<!-- file: crates/ksp-program-api/USAGE.md -->
<!-- version: 1 -->
<!-- version: 2 -->
# Usage de ksp-program-api
Cette page décrit le scaffold public disponible à partir de `0.2.14-pre.002`. Utiliser uniquement les exports du crate-root ; aucun module interne ne fait partie du contrat consommable.
Cette page décrit la surface publique disponible à partir de `0.2.14-pre.003`. Utiliser uniquement les exports du crate-root ; aucun module interne ne fait partie du contrat consommable.
## Construire un input Program avec la façade
@@ -23,9 +23,61 @@ assert!(instruction.is_ok());
`Pubkey`, `ProgramAccountMeta` et `ProgramInstruction` conservent leur ownership Core/Interface. Program API fournit seulement une façade cohérente aux futures implémentations de capability Program.
## Représenter une reconnaissance
```rust
let recognition = ksp_program_api::ProgramInstructionRecognition::ProgramMatch;
match recognition {
ksp_program_api::ProgramInstructionRecognition::NoMatch => {}
ksp_program_api::ProgramInstructionRecognition::ProgramMatch => {}
ksp_program_api::ProgramInstructionRecognition::ExactMatch => {}
_ => {}
}
```
Le wildcard est volontaire : l'enum est `#[non_exhaustive]` afin de ne pas transformer la foundation en vocabulaire fermé pour toujours.
## Représenter un outcome de décodage
Le type décodé reste possédé par l'implémentation :
```rust
struct ExternalDecodedInstruction {
opcode: u8,
}
let outcome = ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(
ExternalDecodedInstruction { opcode: 7_u8 },
);
match outcome {
ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(value) => {
assert_eq!(value.opcode, 7_u8);
}
ksp_program_api::ProgramInstructionDecodeOutcome::Unsupported => {}
_ => {}
}
```
Aucun `Any`, JSON ou enum centrale n'est nécessaire pour transporter ce type.
## Debug sûr
`ProgramInstructionDecodeOutcome<Decoded>` possède un `Debug` volontairement opaque sur la valeur décodée :
```rust
struct SecretDecoded;
let outcome = ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(SecretDecoded);
assert_eq!(std::format!("{outcome:?}"), "Decoded");
```
`SecretDecoded` n'a même pas besoin d'implémenter `Debug`. Cela empêche un diagnostic générique de rendre accidentellement un payload externe.
## Utiliser le contrat d'erreur commun
Les types d'erreur Core sont également disponibles depuis la façade :
Les types d'erreur Core restent disponibles depuis la façade :
```rust
fn forward_result(
@@ -35,21 +87,20 @@ fn forward_result(
}
```
Aucun type d'erreur Program spécifique n'est nécessaire au scaffold.
Le futur `decode(...)` utilisera ce `Result`. Une erreur sera donc `Err(...)`, tandis que `Unsupported` signifie une instruction connue volontairement non prise en charge.
## Ce que `pre.002` ne fournit pas
## Ce que `pre.003` ne fournit pas
Il n'existe encore aucun :
```text
recognize(...)
decode(...)
ProgramInstructionRecognition
ProgramInstructionDecodeOutcome
ProgramInstructionDecoder
program_ids(...)
recognize(...) sur un trait
decode(...) sur un trait
registry de decoders
payload générique JSON/Any
execution preparer
```
Ces éléments ne doivent pas être simulés côté consumer. Les contrats de recognition/outcome puis le trait decoder seront introduits dans leurs tranches dédiées.
Ces éléments ne doivent pas être simulés côté consumer. Le trait decoder et la preuve d'implémentation externe sont réservés à `pre.004`.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-program-api/src/lib.rs
// version: 1
// version: 2
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -7,11 +7,17 @@
//! Open Program contracts shared by KSP and external Program implementations.
//!
//! This initial scaffold exposes only the Core and Interface types selected by
//! the `0.2.14` API model. Decoder behavior, recognition, decode outcomes,
//! registries, codecs, runtime logging and execution preparation are added only
//! by later contracts when their ownership is justified.
//! The foundation exposes Core/Interface types plus the minimal instruction
//! recognition and decode-outcome vocabulary. Decoder behavior, registries,
//! codecs, runtime logging and execution preparation are added only by later
//! contracts when their ownership is justified.
mod program_instruction_decode;
/// Result of a successful Program instruction decode attempt.
pub use self::program_instruction_decode::ProgramInstructionDecodeOutcome;
/// Recognition strength reported by one Program instruction implementation.
pub use self::program_instruction_decode::ProgramInstructionRecognition;
/// Common KSP error type used by Program-facing contracts.
pub use ksp_core_lib::Error;
/// Stable structured code identifying a KSP error category and condition.

View File

@@ -0,0 +1,50 @@
// file: crates/ksp-program-api/src/program_instruction_decode.rs
// version: 1
/// Recognition strength reported by one Program instruction implementation.
///
/// Recognition is intentionally instruction-local. It does not encode registry
/// priority, a persisted proof, a textual discriminator or a global Program
/// kind. [`Self::ExactMatch`] is an assertion made by the implementation for
/// the current instruction, while [`Self::ProgramMatch`] only establishes the
/// Program-level match.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ProgramInstructionRecognition {
/// The implementation does not claim the instruction.
NoMatch,
/// The Program or Program family matches, but the instruction is not proven exact.
ProgramMatch,
/// The implementation claims an exact instruction-local match.
ExactMatch,
}
/// Result of a successful Program instruction decode attempt.
///
/// Decode failures are represented by the surrounding KSP [`crate::Result`],
/// not by a parallel failure variant. `Unsupported` is reserved for a known
/// Program instruction that the implementation deliberately does not decode.
///
/// The custom [`std::fmt::Debug`] implementation never formats the `Decoded`
/// value, so external decoded payloads are not exposed accidentally through
/// generic diagnostics.
#[non_exhaustive]
pub enum ProgramInstructionDecodeOutcome<Decoded> {
/// The instruction was decoded into the implementation-owned output type.
Decoded(Decoded),
/// The instruction is known but unsupported by this decode capability.
Unsupported,
}
impl<Decoded> std::fmt::Debug for ProgramInstructionDecodeOutcome<Decoded> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Decoded(_) => return formatter.write_str("Decoded"),
Self::Unsupported => return formatter.write_str("Unsupported"),
}
}
}
#[cfg(test)]
#[path = "../unit_tests/program_instruction_decode.rs"]
mod tests;

View File

@@ -1,7 +1,7 @@
// file: crates/ksp-program-api/tests/dependency_boundary.rs
// version: 1
// version: 2
//! Dependency and declarative-surface canaries for the Program API scaffold.
//! Dependency and declarative-surface canaries for the Program API foundation.
#[test]
fn pre_002_manifest_has_exact_core_and_interface_runtime_dependencies() {
@@ -46,23 +46,27 @@ fn pre_002_manifest_has_exact_core_and_interface_runtime_dependencies() {
}
#[test]
fn pre_002_crate_root_is_facade_only_without_decoder_runtime_surface() {
fn pre_003_crate_root_adds_only_recognition_and_decode_outcome() {
let crate_root = include_str!("../src/lib.rs");
for required in ["Error", "ErrorCode", "ErrorContext", "Pubkey", "Result", "ProgramAccountMeta", "ProgramInstruction"] {
for required in [
"ProgramInstructionDecodeOutcome",
"ProgramInstructionRecognition",
"Error",
"ErrorCode",
"ErrorContext",
"Pubkey",
"Result",
"ProgramAccountMeta",
"ProgramInstruction",
] {
assert!(crate_root.contains(required), "required Program API facade export missing: {required}");
}
for forbidden in [
"pub mod ",
"ProgramInstructionRecognition",
"ProgramInstructionDecodeOutcome",
"ProgramInstructionDecoder",
"ProgramExecutionPreparer",
"TRACING_TARGET",
"ksp_logging_lib",
"serde",
"Any",
] {
assert!(!crate_root.contains(forbidden), "forbidden pre.002 Program API surface detected: {forbidden}");
for forbidden in ["pub mod ", "ProgramInstructionDecoder", "ProgramExecutionPreparer", "TRACING_TARGET", "ksp_logging_lib", "serde", "Any"] {
assert!(!crate_root.contains(forbidden), "forbidden pre.003 Program API surface detected: {forbidden}");
}
let outcome_source = include_str!("../src/program_instruction_decode.rs");
for forbidden in ["Ignored", "Failed", "serde", "Any"] {
assert!(!outcome_source.contains(forbidden), "forbidden recognition/outcome concept detected: {forbidden}");
}
assert!(!std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/constants.rs").exists());
return;

View File

@@ -1,7 +1,7 @@
// file: crates/ksp-program-api/tests/public_api.rs
// version: 1
// version: 2
//! Integration canaries for the public `ksp-program-api` scaffold.
//! Integration canaries for the public `ksp-program-api` foundation.
fn consume_result(value: ksp_program_api::Result<ksp_program_api::Pubkey>) -> ksp_program_api::Result<ksp_program_api::Pubkey> {
return value;
@@ -31,3 +31,19 @@ fn public_pre_002_scaffold_does_not_require_private_modules() {
assert!(!source.contains("pub mod "));
return;
}
#[test]
fn public_pre_003_recognition_and_decode_outcome_are_available_from_crate_root() {
let recognition = ksp_program_api::ProgramInstructionRecognition::ProgramMatch;
assert_eq!(std::format!("{recognition:?}"), "ProgramMatch");
let decoded = ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(31_u16);
assert_eq!(std::format!("{decoded:?}"), "Decoded");
let decoded_value = match decoded {
ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(value) => value,
_ => 0_u16,
};
assert_eq!(decoded_value, 31_u16);
let unsupported = ksp_program_api::ProgramInstructionDecodeOutcome::<u16>::Unsupported;
assert_eq!(std::format!("{unsupported:?}"), "Unsupported");
return;
}

View File

@@ -0,0 +1,41 @@
// file: crates/ksp-program-api/unit_tests/program_instruction_decode.rs
// version: 1
#[test]
fn recognition_variants_are_distinct_and_payload_free() {
assert_ne!(crate::ProgramInstructionRecognition::NoMatch, crate::ProgramInstructionRecognition::ProgramMatch);
assert_ne!(crate::ProgramInstructionRecognition::ProgramMatch, crate::ProgramInstructionRecognition::ExactMatch);
assert_eq!(std::format!("{:?}", crate::ProgramInstructionRecognition::NoMatch), "NoMatch");
assert_eq!(std::format!("{:?}", crate::ProgramInstructionRecognition::ProgramMatch), "ProgramMatch");
assert_eq!(std::format!("{:?}", crate::ProgramInstructionRecognition::ExactMatch), "ExactMatch");
return;
}
#[test]
fn decode_outcome_preserves_decoded_value_and_unsupported_state() {
let decoded = crate::ProgramInstructionDecodeOutcome::Decoded(17_u64);
let decoded_value = match decoded {
crate::ProgramInstructionDecodeOutcome::Decoded(value) => value,
crate::ProgramInstructionDecodeOutcome::Unsupported => 0_u64,
};
assert_eq!(decoded_value, 17_u64);
let unsupported = crate::ProgramInstructionDecodeOutcome::<u64>::Unsupported;
assert!(matches!(unsupported, crate::ProgramInstructionDecodeOutcome::Unsupported));
return;
}
#[test]
fn decode_outcome_debug_never_requires_or_renders_decoded_debug() {
struct ExternalDecoded {
secret_marker: u8,
}
let decoded = crate::ProgramInstructionDecodeOutcome::Decoded(ExternalDecoded { secret_marker: 0xA7_u8 });
assert_eq!(std::format!("{decoded:?}"), "Decoded");
let secret_marker = match decoded {
crate::ProgramInstructionDecodeOutcome::Decoded(value) => value.secret_marker,
crate::ProgramInstructionDecodeOutcome::Unsupported => 0_u8,
};
assert_eq!(secret_marker, 0xA7_u8);
assert_eq!(std::format!("{:?}", crate::ProgramInstructionDecodeOutcome::<ExternalDecoded>::Unsupported), "Unsupported");
return;
}

285
deltas/0.2.14/pre.003.md Normal file
View File

@@ -0,0 +1,285 @@
<!-- file: deltas/0.2.14/pre.003.md -->
<!-- version: 1 -->
# Delta `0.2.14-pre.003` — recognition + outcome minimal
## 1. Base requise
Cette tranche s'applique exclusivement sur :
```text
v0.2.13
+ 0.2.14-pre.001
+ 0.2.14-pre.002
```
Le gate opérateur fourni pour `pre.002` est intégralement vert :
```text
cargo fmt --all PASS
python3 scripts/audit_rust_workspace_rules.py PASS
python3 scripts/audit_markdown_tables.py ... PASS — 170 tables / 119 fichiers
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS
cargo test -p ksp-program-api PASS
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.2
```
à :
```text
0.2.14-pre.3
```
Commit attendu :
```text
v0.2.14-pre.003
```
## 2. Objectif
Matérialiser uniquement le vocabulaire minimal nécessaire au futur decoder d'instruction :
```text
ProgramInstructionRecognition
ProgramInstructionDecodeOutcome<Decoded>
```
La tranche ne crée toujours aucun comportement de décodage, registry ou contrat d'exécution.
## 3. Recognition
`ProgramInstructionRecognition` est public depuis le crate-root, `#[non_exhaustive]` et possède exactement :
```text
NoMatch
ProgramMatch
ExactMatch
```
Sémantique :
```text
NoMatch l'implémentation ne revendique pas l'instruction
ProgramMatch le Program ou la famille correspond sans preuve instruction-locale exacte
ExactMatch l'implémentation affirme un match instruction-local exact
```
Aucun score, priorité, confidence, proof, surface code ou discriminator textuel n'est introduit.
## 4. Decode outcome
`ProgramInstructionDecodeOutcome<Decoded>` est public depuis le crate-root, `#[non_exhaustive]` et possède exactement :
```text
Decoded(Decoded)
Unsupported
```
La valeur `Decoded` reste possédée par l'implémentation future. Il n'existe aucun `Any`, JSON, payload D3 ou enum centrale pour l'effacer.
`Failed` est volontairement absent : le futur `ProgramInstructionDecoder::decode` retournera le `Result` Core. Une erreur réelle sera donc `Err`, sans deuxième canal de failure.
`Ignored` reste absent : une capability de décodage doit produire une valeur, déclarer l'instruction connue mais unsupported, ou échouer.
## 5. Debug sûr
`ProgramInstructionRecognition` ne transporte aucun payload.
`ProgramInstructionDecodeOutcome<Decoded>` possède une implémentation `Debug` manuelle :
```text
Decoded(_) -> "Decoded"
Unsupported -> "Unsupported"
```
Cette implémentation :
```text
n'impose pas Decoded: Debug
ne formate jamais la valeur Decoded
ne copie aucun payload externe
reste bornée à un nom de variante fixe
```
Le test unitaire utilise volontairement un type externe sans implémentation `Debug` pour prouver cette propriété à la compilation.
## 6. Structure et façade
Un seul module privé est ajouté :
```text
src/program_instruction_decode.rs
```
Le crate-root réexporte :
```text
ProgramInstructionRecognition
ProgramInstructionDecodeOutcome
```
Aucun `pub mod` n'est introduit. Les réexports Core/Interface de `pre.002` restent inchangés.
## 7. Dependency firewall
Le manifest de `ksp-program-api` est inchangé :
```text
ksp-core-lib
ksp-interface-lib
```
Restent interdits et 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
```
Aucun `constants.rs` ou `TRACING_TARGET` n'est justifié pour ces types déclaratifs.
## 8. Tests
### Unit
`unit_tests/program_instruction_decode.rs` vérifie :
```text
variants Recognition distincts
Debug Recognition exact et payload-free
Decoded conserve sa valeur
Unsupported reste distinct
Debug outcome sans Decoded: Debug
Debug outcome ne rend pas la valeur externe
```
### Public API
`tests/public_api.rs` ajoute un canari `pre.003` consommant uniquement :
```text
ksp_program_api::ProgramInstructionRecognition
ksp_program_api::ProgramInstructionDecodeOutcome
```
### Boundary
`tests/dependency_boundary.rs` est avancé pour autoriser uniquement recognition/outcome tout en maintenant l'absence de trait decoder, preparer, logging, serde et module public.
## 9. Fichiers ajoutés
```text
crates/ksp-program-api/src/program_instruction_decode.rs
crates/ksp-program-api/unit_tests/program_instruction_decode.rs
deltas/0.2.14/pre.003.md
```
## 10. 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
```
## 11. 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/**
```
## 12. Scope négatif maintenu
Cette tranche n'introduit pas :
```text
ProgramInstructionDecoder
program_ids(...)
recognize(...) sur un trait
decode(...) sur un trait
associated output contract du trait
external implementation fixture
registry runtime
identity/version/coverage
ProgramAccountDecoder / Event / ReturnData
payload canonique D3
ProgramExecutionPreparer
```
Ces éléments ne doivent pas être anticipés dans un fix de `pre.003`.
## 13. 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.003-fix.NNN` et n'avance pas `pre.004`.
## 14. Suite
Après gate vert, `pre.004` pourra introduire uniquement :
```text
ProgramInstructionDecoder: Send + Sync
associated type Decoded
program_ids(&self) -> &[Pubkey]
recognize(&self, &ProgramInstruction)
decode(&self, &ProgramInstruction) -> Result<ProgramInstructionDecodeOutcome<Self::Decoded>>
external implementation canary avec Pubkey non enregistré
```
Registry runtime, payload canonique D3 et `ProgramExecutionPreparer` resteront hors scope.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/021-V0_2_14_PROGRAM_API_PLAN.md -->
<!-- version: 2 -->
<!-- version: 3 -->
# Plan `0.2.14` — Program API foundation
@@ -541,7 +541,7 @@ Baseline, règles/architecture, héritage, ownership, API candidate, dependency
### `pre.002` — Scaffold `ksp-program-api` + façade + firewall
**Statut : matérialisé ; gate opérateur à confirmer.**
**Statut : alisé ; gate opérateur intégralement PASS.**
La crate est membre du workspace avec exactement `ksp-core-lib` et `ksp-interface-lib` comme dépendances normales. Le crate-root réexporte `Error`, `ErrorCode`, `ErrorContext`, `Result`, `Pubkey`, `ProgramAccountMeta` et `ProgramInstruction`. README/USAGE initiaux et canaris `public_api` / `dependency_boundary` sont présents.
@@ -549,7 +549,9 @@ Aucun trait decoder, recognition, outcome, registry, codec, runtime logging ou e
### `pre.003` — Recognition + outcome minimal
Introduire `ProgramInstructionRecognition` et `ProgramInstructionDecodeOutcome<Decoded>` avec invariants et Debug sûr. Aucun descriptor/registry.
**Statut : matérialisé ; gate opérateur à confirmer.**
`ProgramInstructionRecognition` et `ProgramInstructionDecodeOutcome<Decoded>` sont ajoutés dans un module privé puis réexportés depuis le crate-root. Les deux enums sont `#[non_exhaustive]`. Le `Debug` de l'outcome est manuel, ne requiert pas `Decoded: Debug` et n'affiche jamais la valeur décodée. Aucun descriptor, registry ou trait decoder n'est avancé.
### `pre.004` — `ProgramInstructionDecoder` + external implementation
@@ -599,6 +601,27 @@ dependency firewall canary présent
`pre.003` reste limité à `ProgramInstructionRecognition` et `ProgramInstructionDecodeOutcome<Decoded>` avec leur sémantique et leur Debug sûr. Le trait decoder reste réservé à `pre.004`.
## 13.2 État préparé après `pre.003`
Le gate opérateur `pre.002` fourni le 28 août 2026 est intégralement vert : audits Rust/Markdown, check, Clippy, tests de `ksp-program-api`, workspace complet et graphes Cargo ont été exécutés. Le graphe normal ciblé reste exactement `Core + Interface`.
La tranche `pre.003` matérialise :
```text
ProgramInstructionRecognition NoMatch / ProgramMatch / ExactMatch
ProgramInstructionDecodeOutcome<T> Decoded(T) / Unsupported
non_exhaustive oui sur les deux enums
Debug recognition payload-free par construction
Debug outcome opaque, sans bound T: Debug
Failed / Ignored absents
priority / confidence / proof absents de la surface
ProgramInstructionDecoder absent par contrat pre.003
registry / canonical payload / preparer absents
normal dependencies inchangées : Core + Interface
```
Les tests unitaires vérifient notamment qu'un type décodé externe dépourvu de `Debug` peut être contenu et formaté via l'outcome sans exposer sa valeur. Le passage à `pre.004` reste conditionné au gate opérateur de cet overlay.
## 14. Hors périmètre confirmé
```text

View File

@@ -1,5 +1,5 @@
<!-- file: docs/validation/017-V0_2_14_PROGRAM_API.md -->
<!-- version: 2 -->
<!-- version: 3 -->
# Validation `0.2.14` — Program API foundation
@@ -37,8 +37,8 @@ Le scope validé par `pre.001` est une foundation instruction-only avec output a
| Contrat | Décision `pre.001` | Gate futur |
|--------------------------------------------|---------------------------------------------------------|--------------------------------|
| `ProgramInstructionRecognition` | `NoMatch / ProgramMatch / ExactMatch`, non exhaustif | `pre.003` |
| `ProgramInstructionDecodeOutcome<Decoded>` | `Decoded(Decoded) / Unsupported`, non exhaustif | `pre.003` |
| `ProgramInstructionRecognition` | `NoMatch / ProgramMatch / ExactMatch`, non exhaustif | PASS `pre.003` |
| `ProgramInstructionDecodeOutcome<Decoded>` | `Decoded(Decoded) / Unsupported`, non exhaustif | PASS `pre.003` |
| decoder failure | `ksp_core_lib::Result`, aucun statut `Failed` parallèle | `pre.003` / `pre.004` |
| `ProgramInstructionDecoder` | `Send + Sync`, associated `Decoded` | `pre.004` |
| input | `&ProgramInstruction` | `pre.004` |
@@ -105,8 +105,8 @@ tracing
| unknown Program Pubkey | utilisable sans registry Core | PENDING |
| max Interface input | decoder consomme l'input déjà borné sans nouvelle allocation obligatoire | PENDING |
| malformed program payload | `Err` ou `Unsupported` selon contrat, sans payload dans l'erreur | PENDING |
| Debug recognition | aucun payload | PENDING |
| Debug outcome | contenu `Decoded` non rendu automatiquement | PENDING |
| Debug recognition | aucun payload | PASS |
| Debug outcome | contenu `Decoded` non rendu automatiquement | PASS |
| default methods | aucun default method susceptible de masquer panic/policy | PENDING |
| closed-world enum | aucun inventaire central de Program kinds | PENDING |
| serde accidental | aucune dependency/derive | PENDING |
@@ -157,25 +157,37 @@ prompt de démarrage 0.3.1
delta de la prerelease
```
## 8. État préparé `pre.002`
## 8. Gate opérateur `pre.002`
Le scaffold matérialisé est :
Le gate fourni le 28 août 2026 est intégralement vert :
```text
workspace member PASS structurel
normal dependencies ksp-core-lib + ksp-interface-lib uniquement
crate-root facade Error/ErrorCode/ErrorContext/Result/Pubkey + Interface instruction types
public modules aucun
README / USAGE présents
public API canary présent
dependency firewall canary présent
ProgramInstructionRecognition absent par contrat pre.002
ProgramInstructionDecodeOutcome absent par contrat pre.002
ProgramInstructionDecoder absent par contrat pre.002
registry / canonical payload / preparer absents
serde / codecs / runtime logging absents
cargo fmt --all PASS
audit Rust général / exports / workspace PASS
audit Markdown PASS — 170 tables / 119 fichiers
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS
cargo test -p ksp-program-api PASS — 4 tests d'intégration
cargo test --workspace PASS
cargo tree -p ksp-program-api --edges normal PASS — Core + Interface uniquement
cargo tree --duplicates exécuté, inventaire workspace observé
```
Le gate opérateur `pre.001` fourni le 28 août 2026 est vert pour `cargo fmt --all`, audits Rust/Markdown, `cargo check --workspace` et Clippy workspace.
Ce gate autorise l'ouverture de `pre.003`.
Le passage à `pre.003` reste conditionné au gate opérateur de cet overlay. Les preuves recognition/outcome restent `PENDING` jusqu'à leur tranche dédiée.
## 9. État préparé `pre.003`
| Critère | Statut | Preuve |
|--------------------------------------|--------|----------------------------------------------------------------------|
| `ProgramInstructionRecognition` | PASS | enum non exhaustif `NoMatch / ProgramMatch / ExactMatch` |
| `ProgramInstructionDecodeOutcome<T>` | PASS | enum non exhaustif `Decoded(T) / Unsupported` |
| absence de `Failed` / `Ignored` | PASS | échec réservé au futur `Result`; aucun statut parallèle |
| Debug recognition | PASS | enum sans payload; Debug dérivé |
| Debug outcome | PASS | implémentation manuelle sans `T: Debug`, valeur `Decoded` non rendue |
| dépendances normales | PASS | manifest inchangé : `ksp-core-lib` + `ksp-interface-lib` |
| module public | PASS | aucun `pub mod`; exports crate-root uniquement |
| `ProgramInstructionDecoder` | ABSENT | réservé à `pre.004` |
| registry / descriptor / payload D3 | ABSENT | hors scope maintenu |
| serde / codec / logging / runtime | ABSENT | dependency firewall maintenu |
Les canaris unitaires et publics de `pre.003` doivent passer avant d'ouvrir `pre.004`. La preuve d'implémentation externe, le Program Pubkey open-world et l'associated output complet restent réservés au trait decoder de la tranche suivante.