v0.2.2-pre.001

This commit is contained in:
2026-08-18 06:41:41 +02:00
parent 6c3ecf1f18
commit 9059a2dc45
7 changed files with 690 additions and 9 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/000-README.md -->
<!-- version: 31 -->
<!-- version: 32 -->
# Plans KSP
@@ -17,6 +17,7 @@ Un plan décrit le périmètre, les décisions déjà acquises, les questions ou
- [`006-V0_1_4_CONFIG_DESKTOP_PLAN.md`](006-V0_1_4_CONFIG_DESKTOP_PLAN.md) — plan historique clôturé de la release stable `0.1.4 — ksp-app-config-desk`, établi par `0.1.4-pre.001` puis consolidé jusqu'à `0.1.4-rel.001`.
- [`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 actif de `0.2.2`, établi par `0.2.2-pre.001` après réaudit officiel ; il confirme les 22 méthodes Accounts/Tokens/Cluster, leurs formes wire communes et le sizing de la release.
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: 33 -->
<!-- version: 34 -->
# Séquence des releases fonctionnelles KSP
@@ -390,6 +390,8 @@ Ces trois releases constituent le découpage nominal, pas une obligation de troi
Chaque `pre.001` réaudite la documentation officielle actuelle. Les méthodes Deprecated réellement retirées restent tracées comme historiques/runtime removed au lieu d'être simulées.
`0.2.2-pre.001` a effectué ce réaudit : la partition reste 22 méthodes (5 Accounts + 5 Tokens + 12 Cluster) et le gate de sizing est positif. Le plan d'exécution actif est `docs/plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md`.
## `0.2.5` — Wallet foundation
Mission : créer `ksp-wallet-lib` et le format `.kspwallet`.

View File

@@ -0,0 +1,497 @@
<!-- file: docs/plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md -->
<!-- version: 2 -->
# Plan `0.2.2` — HTTP Accounts + Tokens + Cluster
## Statut
Ce plan ouvre `0.2.2-pre.001` sur la base stable `v0.2.1`.
`0.2.1` a déjà stabilisé la foundation HTTP commune : settings, endpoints/pool/rôles, admission et limites, retry, JSON-RPC 2.0,
registry des méthodes, exécution HTTP générique, adapter Config -> Transport et quatre wrappers typés canari.
Cette release ne reconstruit aucun transport par famille. Elle complète uniquement la surface typée des 22 méthodes déjà attribuées à
`HttpRpcCoverageRelease::V0_2_2`.
## Sources normatives réauditées le 2026-08-17
Source documentaire principale :
```text
https://solana.com/docs/rpc/http
```
Références complémentaires officielles utilisées pour les formes wire communes :
```text
https://solana.com/docs/rpc/json-structures
https://github.com/anza-xyz/agave/blob/v3.1.8/rpc/src/rpc.rs
https://github.com/anza-xyz/agave/blob/v3.1.8/rpc-client-types/src/config.rs
https://github.com/anza-xyz/agave/blob/v3.1.8/rpc-client-types/src/filter.rs
https://github.com/anza-xyz/agave/blob/v3.1.8/rpc-client-types/src/request.rs
https://github.com/anza-xyz/agave/blob/v3.1.8/rpc-client-types/src/response.rs
```
Les liens `Source` des pages RPC Solana consultées pointent actuellement vers Agave `v3.1.8`. KSP ne prend pas de dépendance sur ces crates :
leur source sert seulement à lever les ambiguïtés de la documentation HTTP lorsque nécessaire.
## Résultat de l'audit global
L'index HTTP officiel courant contient toujours **52 méthodes**. Pour les familles concernées par cette release :
```text
Accounts : 6 current = 1 acquis en 0.2.1 + 5 ciblés en 0.2.2
Tokens : 5 current = 5 ciblés en 0.2.2
Cluster : 15 current = 3 acquis en 0.2.1 + 12 ciblés en 0.2.2
```
La navigation officielle Deprecated contient toujours exactement 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 ajoutée, supprimée ou déplacée n'a été détectée par rapport à l'audit `0.2.1` du 2026-08-17. Les 22 méthodes de cette release
restent donc la partition exacte prévue. Les pages courantes ciblées ne portent pas de marqueur Deprecated ou Unstable ; les descriptors KSP
restent `Stable / Supported / Stable`, `Read`, `RetrySafe`.
## Gate de sizing obligatoire
Question :
```text
Les 22 wrappers typés + DTOs partagés + tests + documentation peuvent-ils être clôturés proprement dans cette session ?
```
Réponse :
```text
OUI.
```
Le périmètre ne nécessite ni nouvelle foundation de transport, ni nouvelle crate, ni nouvelle dépendance externe identifiée à `pre.001`.
Les difficultés sont concentrées dans quelques contrats wire, principalement les données de compte, `getProgramAccounts`, `getLeaderSchedule`
et `getVoteAccounts`. Elles peuvent être isolées dans les prereleases prévues sans compresser les tests.
Le sizing reste conditionnel à la règle KSP habituelle : si une tranche dépasse réellement le budget de 1520 minutes, une prerelease
supplémentaire est ajoutée dans `0.2.2`; la release n'est pas artificiellement comprimée et aucune méthode n'est déplacée silencieusement.
## Matrice exacte — Accounts
| Méthode | Paramètres/config confirmés | Résultat typé à préserver | Limites / points significatifs |
|-------------------------------------|-----------------------------------------------------------------|-------------------------------------|---------------------------------------------------------------------------------------------------------|
| `getAccountInfo` | `Pubkey`, config `commitment/encoding/dataSlice/minContextSlot` | `RpcResponse<Option<Account>>` | compte absent = `null`; erreur min-context distincte |
| `getLargestAccounts` | config `commitment/filter/sortResults` | `RpcResponse<Vec<AccountBalance>>` | 20 résultats; filtre `circulating/nonCirculating`; cache provider possible |
| `getMinimumBalanceForRentExemption` | longueur de données, `commitment?` | `u64` lamports | aucune valeur sentinelle inventée |
| `getMultipleAccounts` | `Vec<Pubkey>`, config Account | `RpcResponse<Vec<Option<Account>>>` | maximum documenté 100; ordre des résultats = ordre demandé |
| `getProgramAccounts` | `Pubkey`, config Account + `filters/withContext/sortResults` | résultat bare ou contextualisé | HTTP documente `dataSize`/`memcmp`; Agave accepte aussi `tokenAccountState`; 4 filtres max côté serveur |
### Wire Account commun
Le type `Account` KSP doit rester un DTO de transport, sans décodage Program. Les formes documentées imposent de conserver :
```text
lamports u64
owner Pubkey
executable bool
rentEpoch u64
space Option<u64>
data forme wire discriminée
```
`data` n'est pas modélisable correctement par un simple `String`. Les formes à conserver sont :
- chaîne legacy `binary` lorsque le serveur l'émet ;
- couple `[data, encoding]` pour `base58`, `base64` et `base64+zstd` ;
- objet `jsonParsed` lorsque le parser RPC existe ;
- fallback `[data, "base64"]` même lorsqu'un appel demande `jsonParsed` mais qu'aucun parser n'est disponible.
La valeur `jsonParsed.parsed` reste un `serde_json::Value` au niveau Transport. Aucun modèle SPL/Program n'est introduit ici.
`dataSlice` est un petit DTO `{ offset, length }`. Il n'impose aucun décodage local des données et ne justifie donc pas l'ajout de `base64`
ou `bs58` dans cette release.
### `getProgramAccounts`
Le résultat dépend de `withContext` :
```text
false/absent -> Vec<KeyedAccount>
true -> RpcResponse<Vec<KeyedAccount>>
```
Le contrat public doit refléter cette union au lieu de supprimer le contexte ou d'inventer un contexte lorsque le serveur n'en renvoie pas.
Les filtres KSP doivent distinguer les trois variantes acceptées par la source primaire Agave actuelle :
```text
DataSize(u64)
Memcmp { offset, bytes, encoding }
TokenAccountState
```
Pour `Memcmp`, `base58` est l'encodage implicite lorsque `encoding` est absent; `base58`, `base64` et les octets bruts sont acceptés par le wire
courant, tandis que l'ancien libellé `binary` n'est plus accepté. La donnée comparée est limitée à 128 octets après décodage. Agave expose en
outre `MAX_GET_PROGRAM_ACCOUNT_FILTERS = 4`, même si la page HTTP actuelle ne publie pas cette cardinalité. Le plan retient donc **4 filtres max
comme limite runtime/source primaire à préserver**, en la distinguant explicitement d'une limite publiée sur la page HTTP.
Cette connaissance n'impose pas l'ajout immédiat de `base64` ou `bs58` à KSP : le DTO peut préserver les chaînes encodées sans les décoder.
Une validation locale de la taille décodée ne sera ajoutée que si elle apporte une valeur claire et justifie la dépendance; le wrapper doit en
revanche empêcher plus de quatre filtres, car cette requête serait refusée par le serveur actuel.
## Matrice exacte — Tokens
| Méthode | Paramètres/config confirmés | Résultat typé à préserver | Limites / points significatifs |
|------------------------------|-----------------------------------------------------------------------|-----------------------------------------|-------------------------------------------------------------------|
| `getTokenAccountBalance` | token account `Pubkey`, `commitment?` | `RpcResponse<TokenAmount>` | erreur RPC si le compte n'est pas exploitable comme token account |
| `getTokenAccountsByDelegate` | delegate `Pubkey`, selector `{mint}` ou `{programId}`, config Account | `RpcResponse<Vec<KeyedAccount>>` | selector exclusif par construction |
| `getTokenAccountsByOwner` | owner `Pubkey`, selector `{mint}` ou `{programId}`, config Account | `RpcResponse<Vec<KeyedAccount>>` | selector exclusif par construction |
| `getTokenLargestAccounts` | mint `Pubkey`, `commitment?` | `RpcResponse<Vec<TokenAccountBalance>>` | 20 plus gros comptes du mint |
| `getTokenSupply` | mint `Pubkey`, `commitment?` | `RpcResponse<TokenAmount>` | aucune conversion métier SPL |
Le selector commun doit rendre impossible la production accidentelle d'un objet contenant simultanément `mint` et `programId`, par exemple avec
un enum KSP `Mint(Pubkey) | ProgramId(Pubkey)` sérialisé vers la forme RPC attendue.
Le `TokenAmount` wire commun conserve :
```text
amount String
decimals u8
uiAmount Option<f64>
uiAmountString String
```
`uiAmount` est nullable dans les structures JSON officielles ; KSP ne doit pas le remplacer par `0.0`.
Les deux méthodes de liste réutilisent le DTO Account générique. `jsonParsed` peut contenir des structures SPL, mais Transport les conserve en
JSON sans les convertir en modèle Token métier.
## Matrice exacte — Cluster
| Méthode | Paramètres/config confirmés | Résultat typé à préserver | Limites / points significatifs |
|--------------------------|------------------------------------|---------------------------|---------------------------------------------------------------------------------------------|
| `getClusterNodes` | aucun | `Vec<ClusterNode>` | nombreux endpoints/champs optionnels |
| `getEpochInfo` | config `commitment/minContextSlot` | `EpochInfo` | `transactionCount` nullable |
| `getEpochSchedule` | aucun | `EpochSchedule` | structure fixe d'epoch schedule |
| `getHighestSnapshotSlot` | aucun | `SnapshotSlotInfo` | `incremental` nullable; absence de snapshot = erreur RPC |
| `getIdentity` | aucun | identity `Pubkey` | objet `{identity}` sur le wire |
| `getLeaderSchedule` | slot/config overload | `Option<LeaderSchedule>` | forme des paramètres particulière; résultat nullable |
| `getMaxRetransmitSlot` | aucun | `u64` | lecture simple |
| `getMaxShredInsertSlot` | aucun | `u64` | lecture simple |
| `getSlot` | config `commitment/minContextSlot` | `u64` | erreur min-context possible |
| `getSlotLeader` | config `commitment/minContextSlot` | leader `Pubkey` | erreur min-context possible |
| `getSlotLeaders` | `startSlot`, `limit` | `Vec<Pubkey>` | limite documentée `1..=5000`; ordre par slot |
| `getVoteAccounts` | config vote accounts | `VoteAccountStatus` | `current` + `delinquent`; Agave borne actuellement `epochCredits` à 5 entrées par validator |
### `ClusterNode`
La surface courante documente les champs suivants :
```text
pubkey Pubkey
featureSet Option<u32>
gossip Option<String>
pubsub Option<String>
rpc Option<String>
serveRepair Option<String>
shredVersion Option<u16>
tpu Option<String>
tpuForwards Option<String>
tpuForwardsQuic Option<String>
tpuQuic Option<String>
tpuVote Option<String>
tvu Option<String>
version Option<String>
```
Même si Agave utilise actuellement `SocketAddr` en interne pour plusieurs endpoints, le DTO public Transport ne doit pas imposer davantage que
le contrat JSON. Les adresses réseau restent donc des chaînes wire optionnelles à moins qu'un invariant officiel plus strict soit nécessaire.
### `EpochInfo` et `EpochSchedule`
`EpochInfo` conserve :
```text
absoluteSlot u64
blockHeight u64
epoch u64
slotIndex u64
slotsInEpoch u64
transactionCount Option<u64>
```
`EpochSchedule` conserve :
```text
firstNormalEpoch u64
firstNormalSlot u64
leaderScheduleSlotOffset u64
slotsPerEpoch u64
warmup bool
```
### `getHighestSnapshotSlot`
La page officielle possède les états « Snapshot / No Snapshot ». La source Agave reliée par cette page confirme qu'un noeud sans configuration
snapshot ou sans full snapshot renvoie `RpcCustomError::NoSnapshot`. KSP conserve cette situation comme erreur JSON-RPC applicative ; il ne la
convertit pas en `{ full: 0, incremental: null }`.
### `getLeaderSchedule`
La forme actuelle doit être respectée exactement :
```text
paramètre 1 optionnel : slot u64 | config object | null
paramètre 2 optionnel : config object lorsque le premier paramètre est slot/null
config : commitment + identity
résultat : map identity -> indices de slots relatifs au début de l'epoch, ou null
```
Une API Rust typée doit empêcher les combinaisons incohérentes plutôt que demander à l'appelant de construire manuellement ce tableau JSON.
### `getVoteAccounts`
La config actuelle contient :
```text
commitment
votePubkey
keepUnstakedDelinquents
delinquentSlotDistance
```
Chaque record conserve au minimum :
```text
votePubkey Pubkey
nodePubkey Pubkey
activatedStake u64
commission u8
epochVoteAccount bool
epochCredits Vec<(epoch u64, credits u64, previousCredits u64)>
lastVote u64
rootSlot u64
```
Un petit DTO nommé pour une entrée `epochCredits` est préférable à exposer un tuple public opaque, tout en conservant la forme tableau du wire
au décodage. La source Agave courante limite la réponse à cinq entrées d'historique `epochCredits` par validator; KSP ne doit pas supposer que
l'historique complet d'un vote account est exposé par cette méthode RPC.
## DTOs communs et organisation prévue
`pre.002` doit introduire des types partagés ciblés, pas un « mega DTO ». Le découpage prévu est conceptuellement :
```text
RPC context / commitment
account encoding + data slice + account wire
keyed account
program-account filters
token selector + token amount
cluster epoch/schedule/node/snapshot/leader/vote structures
```
`SolanaCommitment` et `SolanaRpcContext`, actuellement nés avec les canaris `0.2.1`, doivent être mutualisés sans casser leur réexport public.
Les wrappers continueront à utiliser des structs wire privés `serde` puis une conversion explicite vers les DTOs publics KSP, notamment pour
valider et convertir les chaînes de pubkey vers `ksp_core_lib::Pubkey`.
Aucune activation anticipée d'une feature `serde` sur `solana-pubkey` n'est nécessaire : la foundation actuelle sait déjà sérialiser une Pubkey
par sa représentation texte, et les réponses peuvent être décodées par wire structs privés avant conversion.
## Architecture d'exécution imposée
Tous les wrappers suivent :
```text
wrapper typé
-> descriptor central
-> execute_standard_rpc
-> pool/admission
-> reqwest HTTP
-> validation JSON-RPC
-> decode typé
```
Sont explicitement interdits :
- client HTTP parallèle par famille ;
- appel `reqwest` direct dans un wrapper ;
- bypass deadline/admission/retry ;
- dépendance Transport -> Config/Store/Program ;
- import direct de `tracing` ;
- conversion des accounts en modèles Program/SPL métier.
## Erreurs, retry et sécurité
Les 22 descriptors sont réaudités comme lectures et restent `RetrySafe`. Aucun `WriteSubmission` n'entre dans `0.2.2`.
Les règles `0.2.1` sont inchangées :
- erreur JSON-RPC applicative distincte des erreurs transport ;
- `reqwest::Error::without_url()` avant exposition comme source ;
- aucune URL/provider credential/body complet dans diagnostics ou logs ordinaires ;
- retry central uniquement selon descriptor/policy ;
- unique hiérarchie d'erreur via `ksp_core_lib::Error`.
Les validations de paramètres KSP sont ajoutées seulement lorsqu'elles sont normatives et utiles avant I/O, notamment :
```text
getMultipleAccounts : <= 100 pubkeys
getProgramAccounts : <= 4 filtres (limite runtime Agave courante)
getSlotLeaders : 1..=5000
Token selector : exactement mint OU programId
```
`minContextSlot` peut produire l'erreur RPC dédiée `MinContextSlotNotReached`; le wrapper ne doit pas la transformer en absence de données.
## Logging
Aucun événement par méthode n'est ajouté par défaut. Les événements génériques de Transport restent la source d'observabilité : méthode,
rôle, endpoint logique, tentative et statut technique, avec metadata sûre.
Toute instrumentation temporaire `debug` ajoutée pendant les prereleases d'implémentation doit revenir à la baseline `info`/`warn` lors de la
tranche finale.
## Config
L'audit de `std.transport.json`, de son schema et de `ksp-config-lib/src/transport.rs` ne révèle aucune capacité manquante nécessaire aux 22
wrappers. Aucun changement Config n'est prévu dans `pre.001`.
La direction reste strictement :
```text
ksp-config-lib -> ksp-onchain-transport-lib
```
Transport ne lit ni `.env`, ni `std::env`.
## Dépendances
Aucune nouvelle dépendance externe n'est requise par le design retenu à `pre.001`.
En particulier, `base64` et `bs58` ne sont pas nécessaires pour représenter les chaînes encodées du wire. Elles ne seront ajoutées que si une
capacité publique réelle de décodage/validation binaire est décidée ultérieurement, ce qui n'est pas un objectif de `0.2.2`.
Aucune crate SPL, `solana-client`, SDK RPC haut niveau ou `ksp-interface-lib` n'est introduit pour cette surface.
## Stratégie de tests
Chaque wrapper doit disposer de fixtures déterministes et d'un serveur HTTP local couvrant au minimum :
- sérialisation exacte de la request ;
- réponse success typée ;
- `null`/option pertinent ;
- config/overload pertinent ;
- erreur JSON-RPC significative ;
- cardinalité/selector lorsqu'un invariant est documenté.
Cas transversaux prioritaires :
- toutes les variantes Account data réellement supportées ;
- fallback `jsonParsed -> base64` ;
- `space: null` ;
- `getAccountInfo` et `getMultipleAccounts` avec comptes absents ;
- `getProgramAccounts` bare vs `withContext` ;
- `TokenAmount.uiAmount: null` ;
- fields optionnels de `getClusterNodes` ;
- `EpochInfo.transactionCount: null` ;
- `SnapshotSlotInfo.incremental: null` et erreur NoSnapshot ;
- `getLeaderSchedule: null` + overloads ;
- `getVoteAccounts` current/delinquent et epoch credits ;
- limites 100, 4 filtres Program Accounts et 5000; variantes/encodages `memcmp`.
Les canaries de release doivent maintenir :
```text
current methods == 52 exactes
historical methods == 14 exactes
coverage partition == 4 / 22 / 11 / 15
0.2.1 typed canaries == getBalance/getGenesisHash/getHealth/getVersion, inchangés
0.2.2 exact set == les 22 méthodes de ce plan
0.2.3/0.2.4 != typed-complete prématurément
```
Un smoke Devnet opt-in pourra couvrir un sous-ensemble représentatif, sans jamais remplacer les fixtures locales.
## Prévision souple des prereleases
| Tranche | Objectif |
|-----------|--------------------------------------------------------------------------------------------------------------------|
| `pre.001` | audit officiel actuel, matrice exacte, architecture DTO, dépendances et sizing |
| `pre.002` | primitives/configs/results communs Account/Token/Cluster + fixtures de base; mutualiser Context/Commitment |
| `pre.003` | 5 wrappers Accounts + tests déterministes |
| `pre.004` | 5 wrappers Tokens + tests déterministes |
| `pre.005` | Cluster simple : nodes, epoch info/schedule, snapshot, identity, max retransmit, max shred + tests |
| `pre.006` | Cluster schedule/slot/vote : leader schedule, slot, slot leader(s), vote accounts + tests |
| `pre.007` | canaries de complétude, smoke opt-in si utile, README/USAGE, validation finale, prompt `0.2.3`, préparation stable |
Le découpage est volontairement asymétrique : `pre.006` contient moins de méthodes mais les formes les plus complexes. Toute tranche qui dépasse
le budget est scindée en une prerelease supplémentaire plutôt que compressée.
## Hors périmètre
Sont exclus de `0.2.2` :
- les 11 méthodes Transactions de `0.2.3` ;
- les 15 méthodes Blocks + Economics de `0.2.4` ;
- WebSocket, Yellowstone, provider-specific streams ;
- décodage SPL/Program ;
- Store/materialization ;
- `ksp-interface-lib` anticipé ;
- modification de Config sans besoin concret ;
- ajout de dépendances de décodage pour simple représentation wire.
## 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
```
À 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 --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
```
## Critères de clôture `0.2.2`
`0.2.2` ne devient stable que lorsque :
- la matrice officielle est réauditée une dernière fois ;
- les 22 méthodes de ce plan ont chacune un wrapper public typé et leurs tests ;
- les quatre canaris `0.2.1` restent inchangés ;
- la partition globale ne perd aucune méthode future ;
- les DTOs conservent les `null`/options documentés ;
- les frontières de dépendances, redaction, retry et logging restent conformes ;
- README/USAGE et validation durable sont synchronisés ;
- les commandes Cargo finales réellement exécutables passent ;
- le prompt `0.2.3 — HTTP Transactions` est prêt ;
- `rel.001` ne contient plus de développement fonctionnel nouveau.