v0.2.3-pre.001

This commit is contained in:
2026-08-18 10:47:19 +02:00
parent fff69ead9c
commit d06284fca2
7 changed files with 856 additions and 10 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/000-README.md -->
<!-- version: 34 -->
<!-- version: 35 -->
# Plans KSP
@@ -18,6 +18,7 @@ Un plan décrit le périmètre, les décisions déjà acquises, les questions ou
- [`007-V0_2_0_SERIES_PLANNING.md`](007-V0_2_0_SERIES_PLANNING.md) — plan historique clôturé de la release stable `0.2.0`, ouvert par `pre.001`, consolidé par `pre.002`, audité par `pre.003` puis publié par `rel.001`; il fixe l'ordre `0.2.1+`, la stratégie RAW/CORE/DECODE/SPECIALIZED, les vertical slices Program et le prompt `0.2.1`.
- [`008-V0_2_1_ONCHAIN_HTTP_PLAN.md`](008-V0_2_1_ONCHAIN_HTTP_PLAN.md) — plan de `0.2.1`, établi par `0.2.1-pre.001`, recalibré par `pre.001-fix.001` et amené en clôture candidate par `pre.007`; il conserve l'inventaire 52 méthodes HTTP courantes + 14 Deprecated historiques, le design Transport/Config et le split de couverture typée sur `0.2.1``0.2.4`.
- [`009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md`](009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md) — plan clôturé de la release stable `0.2.2`, établi par `pre.001`, corrigé après réaudit Agave v4.2.1 puis exécuté jusqu'à `pre.007-fix.002`; il couvre les 22 wrappers Accounts/Tokens/Cluster, le smoke Transport opt-in et la préparation de `0.2.3`.
- [`010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md`](010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md) — plan actif de `0.2.3 — HTTP Transactions`, ouvert par `pre.001`; il confirme les 11 méthodes, la classification `8 Read / 2 WriteSubmission / 1 Simulation`, les wires transactionnels et le no-resend après dispatch ambigu.
Le `pre.001` de chaque release fonctionnelle peut introduire son propre plan détaillé lorsque la release s'ouvre.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md -->
<!-- version: 36 -->
<!-- version: 37 -->
# Séquence des releases fonctionnelles KSP
@@ -392,6 +392,8 @@ Chaque `pre.001` réaudite la documentation officielle actuelle. Les méthodes D
`0.2.2-pre.001` a confirmé la partition de 22 méthodes et son fix documentaire a recoupé les formes wire avec Agave v4.2.1. Les tranches `pre.002``pre.006` ont livré les DTOs puis les 5 Accounts, 5 Tokens et 12 Cluster. `pre.007`, puis `pre.007-fix.001` et `pre.007-fix.002`, ont fermé les canaries exactes 22/22, le smoke Devnet Transport pur, README/USAGE, la matrice de validation `004` et le prompt `0.2.3`. `0.2.2-rel.001` publie cette surface stable après validation du workspace et des deux smokes Devnet. Le smoke cross-crates Config -> Transport de `0.2.1` reste séparé et transitoire.
`0.2.3-pre.001` réaudite le 2026-08-18 la catégorie Transactions contre la documentation Solana actuelle et Agave v4.2.1 : les 11 méthodes prévues restent exactes, la classification `8 Read / RetrySafe`, `2 WriteSubmission / NeverAfterDispatch` et `1 Simulation / RetrySafe` reste correcte, et le gate de sizing est positif. Aucun `base64`, `bs58`, `wincode` ni client RPC Solana supplémentaire nest ajouté à louverture : les payloads sérialisés restent opaques dans Transport tant quun besoin de décodage local nest pas démontré. Le plan détaillé actif est `docs/plans/010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md`.
## `0.2.5` — Wallet foundation
Mission : créer `ksp-wallet-lib` et le format `.kspwallet`.

View File

@@ -0,0 +1,636 @@
<!-- file: docs/plans/010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md -->
<!-- version: 2 -->
# Plan `0.2.3` — HTTP Transactions
## Statut
Ce plan est ouvert par `0.2.3-pre.001` sur la base stable `v0.2.2`.
`0.2.1` a stabilisé la foundation HTTP Solana et quatre wrappers typés canari. `0.2.2` a ajouté les 22 wrappers Accounts/Tokens/Cluster,
les DTOs wire communs, le smoke Devnet Transport pur et les canaries exactes de complétude.
La release `0.2.3` complète uniquement les **11 méthodes Transactions** déjà affectées à `HttpRpcCoverageRelease::V0_2_3`. Elle ne crée ni
client HTTP parallèle, ni executor métier de transaction, ni dépendance Transport -> Config/Store/Program.
## Sources normatives réauditées le 2026-08-18
Sources documentaires principales :
```text
https://solana.com/docs/rpc/http
https://solana.com/docs/rpc/json-structures
```
Pages Transaction réauditées :
```text
https://solana.com/docs/rpc/http/getfeeformessage
https://solana.com/docs/rpc/http/getlatestblockhash
https://solana.com/docs/rpc/http/getrecentprioritizationfees
https://solana.com/docs/rpc/http/getsignaturesforaddress
https://solana.com/docs/rpc/http/getsignaturestatuses
https://solana.com/docs/rpc/http/gettransaction
https://solana.com/docs/rpc/http/gettransactioncount
https://solana.com/docs/rpc/http/isblockhashvalid
https://solana.com/docs/rpc/http/requestairdrop
https://solana.com/docs/rpc/http/sendtransaction
https://solana.com/docs/rpc/http/simulatetransaction
```
Sources primaires Agave utilisées pour lever les ambiguïtés du wire et des limites runtime :
```text
https://github.com/anza-xyz/agave/blob/v4.2.1/rpc/src/rpc.rs
https://github.com/anza-xyz/agave/blob/v4.2.1/rpc-client-types/src/config.rs
https://github.com/anza-xyz/agave/blob/v4.2.1/rpc-client-types/src/request.rs
https://github.com/anza-xyz/agave/blob/v4.2.1/rpc-client-types/src/response.rs
https://github.com/anza-xyz/agave/blob/v4.2.1/transaction-status-client-types/src/lib.rs
```
Les liens `Source` du site RPC Solana consultés pointent encore vers Agave `v3.1.8` et les exemples affichent encore `apiVersion: 3.1.8`.
Comme pour la clôture `0.2.2`, KSP recoupe donc la documentation publique avec Agave `v4.2.1`, dont le workspace déclare la version `4.2.1`,
sans prendre de dépendance sur ses crates RPC/client.
## Audit global et périmètre confirmé
L'index HTTP Solana courant contient toujours **52 méthodes** et sa catégorie Transactions contient exactement :
```text
getFeeForMessage
getLatestBlockhash
getRecentPrioritizationFees
getSignaturesForAddress
getSignatureStatuses
getTransaction
getTransactionCount
isBlockhashValid
requestAirdrop
sendTransaction
simulateTransaction
```
La navigation Deprecated officielle conserve les **14 méthodes historiques** déjà enregistrées dans KSP :
```text
confirmTransaction
getConfirmedBlock
getConfirmedBlocks
getConfirmedBlocksWithLimit
getConfirmedSignaturesForAddress2
getConfirmedTransaction
getFeeCalculatorForBlockhash
getFeeRateGovernor
getFees
getRecentBlockhash
getSignatureConfirmation
getSignatureStatus
getSnapshotSlot
getStakeActivation
```
Aucune méthode Transaction n'est ajoutée, supprimée ou déplacée par rapport à la partition KSP stable `0.2.2` :
```text
0.2.1 exact = 4
0.2.2 exact = 22
0.2.3 exact = 11
0.2.4 reste = 15
```
Les onze pages courantes ciblées restent des méthodes HTTP courantes. `getTransaction` conserve une **forme de requête moderne** par objet de
configuration et une **forme legacy de compatibilité** où le second paramètre est directement la chaîne d'encoding ; la documentation actuelle
marque explicitement cette forme bare comme dépréciée et recommande l'objet.
## Gate de sizing obligatoire
Question :
```text
Les 11 wrappers Transactions, leurs DTOs/wires, les règles write/simulation, les tests et la documentation peuvent-ils être clôturés proprement dans cette session ?
```
Réponse :
```text
OUI.
```
Le périmètre est plus petit que `0.2.2`, mais les méthodes sont plus hétérogènes. Le sizing reste acceptable en isolant les trois zones coûteuses :
1. `getTransaction` et son wire encoding/version/meta ;
2. `requestAirdrop` + `sendTransaction` et les preuves de no-resend après dispatch ambigu ;
3. `simulateTransaction` et son résultat riche.
Aucun split de release n'est donc nécessaire à `pre.001`. Si une tranche réelle dépasse 1520 minutes, une prerelease supplémentaire est
ajoutée dans `0.2.3` sans déplacer silencieusement une méthode vers `0.2.4`.
## Classification de sécurité confirmée
La metadata KSP existante reste correcte après réaudit :
```text
8 Read / RetrySafe
2 WriteSubmission / NeverAfterDispatch : requestAirdrop, sendTransaction
1 Simulation / RetrySafe : simulateTransaction
```
La règle centrale reste normative :
> une `WriteSubmission` ne doit jamais être resoumise automatiquement lorsque KSP ne peut pas prouver que la tentative précédente n'a pas été dispatchée.
Un timeout après envoi, un HTTP temporaire reçu après dispatch, un rate-limit reçu après dispatch ou une rupture dont l'état de dispatch est
ambigu arrêtent donc la boucle de retry Transport pour `requestAirdrop` et `sendTransaction`.
Une erreur explicitement classée `NotDispatched` peut encore suivre la policy centrale existante ; aucun wrapper write ne possède sa propre boucle.
Le champ RPC `sendTransaction.maxRetries` est **distinct** : il configure les retransmissions du noeud RPC après acceptation de la requête et ne
constitue jamais une autorisation de retry HTTP côté KSP.
`simulateTransaction` n'émet pas la transaction sur le réseau et reste `Simulation / RetrySafe` dans la metadata Transport.
## Flux architectural obligatoire
Tous les wrappers de cette release suivent le flux unique :
```text
wrapper typed
-> descriptor central
-> execute_standard_rpc
-> pool/admission
-> executor HTTP
-> validation JSON-RPC
-> decode typed
```
Restent interdits :
```text
wrapper -> reqwest direct
wrapper -> nouveau client HTTP
wrapper -> retry/deadline/admission bypass
Transport -> Config
Transport -> Store
Transport -> Program
Transport -> tracing direct
```
Les wrappers réutilisent `SolanaCommitment`, `SolanaContextConfig`, `SolanaRpcContext`, `SolanaRpcResponse<T>` et les erreurs KSP existantes lorsque
leur contrat s'applique.
## Décision dépendances et payloads sérialisés
### Décision `pre.001`
Aucune nouvelle dépendance n'est ajoutée :
```text
base64 NON
bs58 NON
wincode NON
solana-client NON
crate RPC SDK NON
```
Motif : les trois entrées sérialisées de cette release peuvent être transportées comme **chaînes encodées opaques** accompagnées d'un encoding
typé. KSP n'a pas besoin de désérialiser un message ou une transaction pour construire la requête, appliquer la policy de retry, préserver le
wire ou décoder la réponse.
Agave réalise des contrôles de décodage, de désérialisation, de sanitization et de taille de transaction. Les reproduire complètement côté KSP
impliquerait davantage que `base64`/`bs58` : cela rapprocherait Transport d'un codec transactionnel et d'un SDK wire complet sans besoin fonctionnel
établi. `pre.001` ne crée donc pas cette responsabilité.
KSP applique en revanche avant I/O les invariants qui ne nécessitent aucun décodage du payload : cardinalités documentées/runtime, encodings
acceptés par l'opération, formes de config, incompatibilités de flags et paramètres structuraux.
Cette décision peut être réouverte dans une future tranche si un besoin concret exige une validation locale des bytes sérialisés. L'ajout devra
alors être borné, déclaré dans `[workspace.dependencies]` et justifié par un test qui démontre la valeur de la validation locale.
### Encodings à distinguer
Pour les **entrées** `sendTransaction` et `simulateTransaction`, la source Agave actuelle accepte les encodings binaires `base58` et `base64` ;
les encodings JSON ne sont pas des formats d'entrée valides pour ces deux opérations.
`getFeeForMessage` reçoit une chaîne base64. La page publique documente les messages legacy et v0. KSP ne parse pas la version du message et ne
fige donc pas artificiellement le type public sur une liste de versions sérialisées que le runtime pourrait faire évoluer.
Pour la **sortie** `getTransaction`, le wire doit rester plus large : `base58`, `base64`, `json`, `jsonParsed`, ainsi que les formes legacy encore
acceptées/auditées. Les formes encodées et JSON ne doivent pas être écrasées dans un seul `String` ambigu.
## Matrice exacte des 11 méthodes
| Méthode | Paramètres ordonnés / config | Résultat à préserver | Validation / particularités |
|-------------------------------|-------------------------------------------------------|--------------------------------------------------|--------------------------------------------------------------------------------|
| `getFeeForMessage` | `messageBase64`, `SolanaContextConfig?` | `SolanaRpcResponse<Option<u64>>` | message base64 opaque ; `null` préservé |
| `getLatestBlockhash` | `SolanaContextConfig?` | contexte + `{ blockhash, lastValidBlockHeight }` | blockhash wire non vide ; contexte conservé |
| `getRecentPrioritizationFees` | `Vec<Pubkey>?` | `Vec<{ slot, prioritizationFee }>` | maximum 128 adresses ; ordre serveur conservé |
| `getSignaturesForAddress` | `Pubkey`, config pagination | `Vec<SignatureInfo>` | `limit` `1..=1000`; newest -> oldest; nulls préservés |
| `getSignatureStatuses` | `Vec<signature>`, config history? | contexte + `Vec<Option<SignatureStatus>>` | maximum 256; positions/nulls conservés; tableau vide accepté par Agave courant |
| `getTransaction` | signature, config moderne **ou** encoding bare legacy | `Option<ConfirmedTransaction>` | transaction absente = `null`; encoding/version/meta lossless |
| `getTransactionCount` | `SolanaContextConfig?` | `u64` | forme simple, config contextuelle |
| `isBlockhashValid` | blockhash, `SolanaContextConfig?` | `SolanaRpcResponse<bool>` | blockhash passé tel quel au runtime sauf invariant KSP non ambigu |
| `requestAirdrop` | `Pubkey`, lamports, config? | signature | `WriteSubmission`; aucun resend après dispatch ambigu |
| `sendTransaction` | transaction encodée, config? | première signature | `base58/base64`; `maxRetries` est node-side; no-resend KSP |
| `simulateTransaction` | transaction encodée, config? | contexte + résultat simulation | `base58/base64`; `sigVerify` incompatible avec `replaceRecentBlockhash` |
## DTOs et wires communs prévus
Les noms Rust exacts restent ajustables à `pre.002`, mais les responsabilités suivantes sont figées.
### Blockhash et latest blockhash
Le résultat de `getLatestBlockhash` conserve :
```text
blockhash String/newtype wire KSP
lastValidBlockHeight u64
```
Le même petit objet `{ blockhash, lastValidBlockHeight }` peut être réutilisé lorsque `simulateTransaction` retourne un `replacementBlockhash`.
Aucune primitive cryptographique supplémentaire n'est nécessaire pour le transporter.
### Signature information
`getSignaturesForAddress` conserve au minimum :
```text
signature String/newtype KSP
slot u64
err JSON transaction error nullable
memo Option<String>
blockTime Option<i64>
confirmationStatus Option<Processed|Confirmed|Finalized>
transactionIndex Option<u32>
```
La page HTTP actuelle ne liste pas `transactionIndex`, mais Agave `v4.2.1` l'expose comme champ optionnel avec `serde(default)` et omission lorsque
absent. KSP doit donc l'accepter sans le rendre obligatoire, afin de ne pas perdre une donnée runtime actuelle ni casser les providers qui ne
l'émettent pas encore.
`err` reste un contrat transaction-error wire et ne devient pas un modèle Program. Une représentation `serde_json::Value` bornée à cette frontière
est acceptable tant que KSP ne possède pas encore le codec générique des erreurs de transaction.
### Signature status
`getSignatureStatuses` préserve l'ordre exact du tableau d'entrée. Chaque position de sortie est :
```text
Option<SignatureStatus>
```
Un statut présent conserve :
```text
slot u64
confirmations Option<usize/u64>
err transaction error nullable
status forme legacy de résultat, encore présente sur le wire
confirmationStatus Option<Processed|Confirmed|Finalized>
```
La forme `status` historique à l'intérieur de l'objet courant n'est pas confondue avec les anciennes **méthodes RPC** Deprecated du registre.
KSP peut la préserver de manière lossless sans la recommander comme nouvelle API métier.
### Transaction encoding/config
Le contrat moderne `getTransaction` doit disposer d'un objet de config contenant :
```text
commitment Option<SolanaCommitment>
encoding Option<TransactionEncoding>
maxSupportedTransactionVersion Option<u8>
```
La compatibilité auditée garde en plus une forme legacy explicite :
```text
getTransaction(signature, "<encoding>")
```
Cette API legacy doit être nommée/annotée de manière à ne pas sembler être la voie recommandée. Le descriptor existant
`StableWithDeprecatedLegacy` reste correct.
Le résultat confirmé garde un top-level typé :
```text
slot u64
blockTime Option<i64>
transaction EncodedTransaction
meta Option<transaction-meta wire>
version legacy | number
```
`EncodedTransaction` doit préserver les différentes formes réellement sérialisées :
```text
objet JSON/jsonParsed
chaîne legacy binaire
[string, "base58" | "base64"]
```
KSP ne doit pas dupliquer tout `solana-transaction-status-client-types` pour obtenir cette union. Les sous-structures transaction/message/meta qui
sont riches, évolutives ou dépendantes de `jsonParsed` peuvent rester lossless via des DTOs étroits + `serde_json::Value` aux frontières prévues.
Le meta doit au minimum respecter la distinction **absent/null/présent** imposée par le wire et ne jamais inventer des listes ou zéros lorsque le
provider omet une donnée optionnelle.
### `getRecentPrioritizationFees`
Le paramètre optionnel est un tableau d'adresses. La documentation actuelle fixe un maximum de **128** et précise que, lorsqu'il est fourni, les
samples correspondent aux transactions ayant verrouillé toutes ces adresses en writable. La cache d'un noeud conserve actuellement jusqu'à
150 blocs de données de prioritization fees ; ce nombre décrit le runtime/cache et n'est pas une cardinalité de réponse à imposer côté client.
Le résultat conserve simplement :
```text
slot u64
prioritizationFee u64
```
### Pagination `getSignaturesForAddress`
La config prévue conserve :
```text
commitment Option<SolanaCommitment>
minContextSlot Option<u64>
limit Option<usize>
before Option<String/newtype signature>
until Option<String/newtype signature>
```
Agave `v4.2.1` applique `1000` par défaut et refuse `limit == 0` ou `limit > 1000`. KSP peut donc rejeter cette cardinalité avant I/O sans dépendance
supplémentaire.
### `getSignatureStatuses`
La requête conserve :
```text
signatures Vec<String/newtype signature>
searchTransactionHistory Option<bool>
```
La documentation et Agave bornent le tableau à **256** signatures. La source actuelle refuse uniquement `> 256`; un tableau vide reste donc une
forme valide à ne pas interdire arbitrairement.
Quand `searchTransactionHistory` est absent/faux, le noeud recherche seulement son cache récent. KSP transporte l'option sans transformer
silencieusement l'appel en recherche historique coûteuse.
## Write submissions
### `requestAirdrop`
La config courante conserve :
```text
commitment Option<SolanaCommitment>
recentBlockhash Option<String>
```
Le résultat est une signature de transaction. L'appel déclenche la création/soumission d'une transaction de faucet : il reste donc
`WriteSubmission / NeverAfterDispatch` même si la réponse n'est qu'une signature.
Aucun retry ad hoc n'est autorisé dans le wrapper. Un résultat ambigu après dispatch remonte au caller.
### `sendTransaction`
Transport reçoit une transaction **déjà construite et signée**. Il ne devient ni builder, ni signer, ni executor métier.
La config courante conserve :
```text
encoding Option<Base58|Base64>
skipPreflight Option/default false
preflightCommitment Option<SolanaCommitment>
maxRetries Option<usize>
minContextSlot Option<u64>
```
Le runtime RPC vérifie normalement les signatures et simule la transaction avant relay lorsque `skipPreflight` est faux. Une réponse réussie ne
constitue pas une confirmation on-chain ; le résultat est la première signature embarquée dans la transaction.
La distinction de retry doit rester explicite :
```text
sendTransaction.maxRetries = retry/retransmission du noeud RPC
KSP HttpRetrySettings = retry de la requête HTTP
```
La première peut être configurée par l'appel. La seconde reste bloquée après tout dispatch ambigu grâce au descriptor central.
## `simulateTransaction`
La simulation conserve les options Agave actuelles :
```text
commitment Option<SolanaCommitment>
encoding Option<Base58|Base64>
replaceRecentBlockhash bool/default false
sigVerify bool/default false
minContextSlot Option<u64>
innerInstructions bool/default false
accounts Option<SimulationAccountsConfig>
```
L'invariant runtime courant :
```text
sigVerify == true && replaceRecentBlockhash == true -> invalid params
```
KSP doit le rejeter avant I/O, car il est déterministe et ne nécessite aucun décodage de la transaction.
La sous-config `accounts` conserve l'encoding Account compatible et un tableau d'adresses. Agave rejette actuellement les encodings Account
`binary/base58` pour ce retour et borne dynamiquement le nombre demandé au nombre de comptes de la transaction. Comme KSP ne décode pas la
transaction en `0.2.3`, cette limite dynamique reste une validation runtime/provider et n'est pas réimplémentée avec une dépendance transactionnelle.
Le résultat de simulation Agave `v4.2.1` contient notamment, sous forme optionnelle lorsque pertinente :
```text
err
logs
accounts
unitsConsumed
loadedAccountsDataSize
returnData
innerInstructions
replacementBlockhash
fee
preBalances
postBalances
preTokenBalances
postTokenBalances
loadedAddresses
```
Transport doit préserver ces champs et leurs `null`/absences sans décoder les instructions ou retours Program en modèles métier.
## Erreurs et validations avant I/O
Les wrappers réutilisent le domaine d'erreur KSP. Les contrôles locaux ciblés comprennent au minimum :
```text
getRecentPrioritizationFees : <= 128 adresses
getSignaturesForAddress : limit 1..=1000 quand fourni
getSignatureStatuses : <= 256 signatures
sendTransaction : encoding d'entrée binaire supporté
simulateTransaction : encoding d'entrée binaire supporté
simulateTransaction : sigVerify XOR replaceRecentBlockhash pour le couple interdit
```
Les validations nécessitant de décoder/sanitizer les bytes de message/transaction restent côté RPC pour cette release.
Les erreurs JSON-RPC significatives — invalid params, min-context non atteint, transaction non trouvée sous forme `null`, preflight failure,
transaction history indisponible, unsupported transaction version — doivent être couvertes par fixtures lorsque la méthode correspondante peut
les produire. KSP ne convertit pas une erreur RPC applicative en retry de transport.
## Stratégie de tests
Chaque wrapper possède des fixtures HTTP locales déterministes qui vérifient selon son contrat :
- méthode et tableau `params` exacts, dans l'ordre exact ;
- omission correcte du paramètre config lorsqu'il est absent ;
- success typed ;
- `null` / champ optionnel / champ omis ;
- erreur JSON-RPC ;
- cardinalité rejetée avant I/O lorsque KSP la connaît sans décodage ;
- forme legacy `getTransaction` séparée de la forme moderne ;
- `maxSupportedTransactionVersion` ;
- encodings transaction ;
- positions `null` de `getSignatureStatuses` ;
- ordre newest-first de `getSignaturesForAddress` ;
- champs de simulation riches et optionnels.
### Tests write/no-resend
Les tests `requestAirdrop` et `sendTransaction` ne doivent pas se contenter de tester la fonction pure `evaluate_transport_retry` déjà existante.
Ils doivent exercer le chemin wrapper -> executor avec un serveur fixture capable de compter les requêtes et démontrer au minimum :
```text
connection failure prouvée NotDispatched -> policy centrale applicable
HTTP 429 après dispatch -> une seule soumission write
HTTP 5xx temporaire après dispatch -> une seule soumission write
timeout/issue ambiguë après dispatch -> aucune seconde soumission automatique
```
L'objectif n'est pas de créer une policy parallèle dans les wrappers, mais de prouver que les descriptors write utilisent réellement la policy
centrale dans l'exécution standard.
### Simulation
`simulateTransaction` reste retry-safe. Les fixtures doivent donc vérifier la config, l'incompatibilité `sigVerify/replaceRecentBlockhash`, les
résultats partiels/nullables et au moins un scénario de retry transport sûr déjà supporté par l'executor commun.
## Canaries de complétude à conserver
À toute la release :
```text
current == 52
historical == 14
0.2.1 exact == 4
0.2.2 exact == 22
0.2.3 exact == 11
0.2.4 exact == 15
```
Aucun wrapper `0.2.4` n'est déclaré typed-complete avant sa release.
La canarie `0.2.3 exact == 11` ne devient réellement verte qu'une fois les onze wrappers publics et leurs tests de surface en place. Le registry
peut déjà contenir leurs descriptors sans que cela ne compte comme couverture typée.
## Smokes live
Les fixtures locales restent normatives pour la correctness des wrappers. Les smokes live sont opt-in et ne les remplacent pas.
La décision finale de smoke est réservée à la prerelease de clôture. Toute extension doit respecter l'ownership déjà stabilisée :
- un smoke **Transport pur** qui construit ses settings programmatiquement peut rester sous `ksp-onchain-transport-lib` ;
- un nouveau smoke cross-crates `Config + Transport + ...` ne doit pas être ajouté sous Config comme destination générale ;
- `requestAirdrop` et `sendTransaction` ne seront pas exercés live sans justification forte, car un smoke de lecture/simulation suffit à valider la
route Transaction sans introduire d'effet de bord ou de dépendance faucet/provider.
Un candidat raisonnable pour la clôture est un smoke Transport pur combinant `getLatestBlockhash` et une lecture Transaction déterministe ; une
simulation live n'est retenue que si une fixture transactionnelle stable peut être fournie sans déplacer la construction/signature métier dans
Transport.
## Prévision souple des prereleases
```text
pre.001 audit officiel/Agave + matrice + sécurité + wire/deps + sizing
pre.002 primitives/configs/results Transactions partagés + fixtures communes
pre.003 getFeeForMessage + getLatestBlockhash + getTransactionCount + isBlockhashValid
pre.004 getRecentPrioritizationFees + getSignaturesForAddress + getSignatureStatuses
pre.005 getTransaction moderne + compatibilité legacy + transaction/meta/version wire
pre.006 requestAirdrop + sendTransaction + preuves end-to-end no-resend
pre.007 simulateTransaction + résultat riche + invariants de config
pre.008 réaudit final + canaries + smoke opt-in si pertinent + README/USAGE + validation + prompt 0.2.4
```
Une prerelease intermédiaire peut être ajoutée si le volume réel d'une tranche dépasse le budget. La dernière prerelease reste une tranche de
clôture documentaire/validation et ne doit pas devenir une implémentation massive tardive.
## Critères de clôture de `0.2.3`
La release ne peut être candidate stable que si :
- les 11 wrappers sont publics et passent tous par `execute_standard_rpc` ;
- les trois classes de sécurité restent exactes `8 Read / 2 WriteSubmission / 1 Simulation` ;
- `requestAirdrop` et `sendTransaction` prouvent l'absence de resend automatique après dispatch ambigu ;
- `getTransaction` préserve la forme moderne et la compatibilité legacy explicitement dépréciée ;
- `maxSupportedTransactionVersion` est préservé ;
- les cardinalités 128 / 1000 / 256 sont testées selon leur contrat exact ;
- les tableaux et `null` positionnels sont préservés ;
- `simulateTransaction` préserve son config/result sans décodage Program ;
- les canaries 52/14/4/22/11/15 passent ;
- aucune dépendance Transport -> Config/Store/Program/tracing direct n'est introduite ;
- README/USAGE, plan, matrice de validation et prompt `0.2.4` sont synchronisés ;
- les validations Cargo de clôture et graphes de dépendances ont une preuve opérateur ou une exécution réelle ;
- `CHANGELOG.md` reste réservé à `0.2.3-rel.001` ;
- la release stable finale reste strictement publicationnelle.
## Validations prévues pendant le développement
```bash
cargo fmt --all
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-onchain-transport-lib
```
## Validations prévues à la clôture
```bash
cargo fmt --all
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-onchain-transport-lib
cargo test -p ksp-config-lib
cargo test -p ksp-core-lib
cargo test -p ksp-app-config-desk
cargo test --workspace
cargo tree -p ksp-onchain-transport-lib
cargo tree -p ksp-onchain-transport-lib -d
cargo tree -p ksp-onchain-transport-lib -e features
cargo tree -p ksp-onchain-transport-lib -e normal
```
Aucune commande n'est déclarée réussie sans exécution réelle ou preuve opérateur.
## Résultat de `pre.001`
`pre.001` s'arrête volontairement avant toute source Rust fonctionnelle :
- périmètre 11/11 confirmé ;
- classification sécurité confirmée ;
- architecture commune confirmée ;
- wire/config/result audités ;
- limites locales/runtime distinguées ;
- aucune nouvelle dépendance justifiée ;
- gate de sizing positif ;
- découpage `pre.002``pre.008` défini.
La tranche suivante peut commencer par les primitives wire communes sans réouvrir le scope de release.