# Validation `0.2.11` — Off-chain price transport SOL/USD ## 1. Objet Cette matrice suit la release `0.2.11 — Off-chain price transport` ouverte par `pre.001`. Le contrat fonctionnel V1 est : ```text SOL/USD uniquement plusieurs providers gratuits admis HTTP REST reqwest uniquement aucun SDK provider provider/rate-limit/availability possédés par ksp-offchain-transport-lib Config -> Off-chain Transport sans dépendance inverse future ksp-app-solprices-desk purement HID et provider-agnostic ``` Les statuts `PLANNED` restent non validés tant que la tranche correspondante n'est pas exécutée. ## 2. Gate `0.2.11-pre.001` | Critère | Statut | Preuve | |---------------------------------------------------------------|--------|-----------------------------------------------------------------------------------------| | archive stable `v0.2.10` utilisée | PASS | archive Gitea fournie par l'opérateur | | `workspace.package.version = 0.2.10` à l'entrée | PASS | inspection `Cargo.toml` | | `deltas/0.2.10/rel.001.md` présent | PASS | inspection archive | | `prompts/016-V0_2_11_START_PROMPT.md` présent | PASS | inspection archive | | `.git` absent de l'archive | PASS | inspection filesystem | | `ksp-offchain-transport-lib` absent à l'entrée | PASS | inventaire workspace | | primitives reqwest/serde/serde_json/tokio/http déjà présentes | PASS | inventaire `[workspace.dependencies]` | | audit providers gratuit/live actuel | PASS | sources officielles CoinGecko, CMC, CoinPaprika, Kraken, Coinbase, Jupiter, Birdeye, DS | | conditions d'usage gratuites distinguées du transport | PASS | plan 018, gratuité technique distincte de licence commerciale durable | | SOL/USD retenu comme seule paire V1 | PASS | décision opérateur + plan 018 | | aucun SDK provider retenu | PASS | décision opérateur + plan 018 | | huit providers retenus pour implémentation | PASS | plan 018 section provider | | DexScreener V1 limité à une paire SOL/USD configurée | PASS | décision opérateur + plan 018 | | future `ksp-app-solprices-desk` purement HID | PASS | décision opérateur + architecture | | ownership rate limits/availability dans Off-chain Transport | PASS | décision opérateur + plan 018 | | threat model V1 établi | PASS | plan 018 | | forecast prereleases établi | PASS | plan 018 | | code runtime provider ajouté en `pre.001` | N/A | interdit par le gate, aucune crate créée | ## 3. Gate `0.2.11-pre.002` | Critère | Statut | Preuve | |----------------------------------------------------|--------|----------------------------------------------------------------| | crate `ksp-offchain-transport-lib` créée | PASS | membre workspace + package dédié | | version workspace synchronisée sur `0.2.11-pre.2` | PASS | `Cargo.toml` racine | | dépendance inverse vers Config absente | PASS | manifest crate | | `reqwest` et SDK providers absents de la fondation | PASS | manifest crate | | `MarketPriceDecimal` exact sans canon `f64` | PASS | coefficient `u128`, scale 18, sérialisation string | | SOL/USD seule paire publique V1 | PASS | `MarketPricePair::SolUsd` + `MarketPriceObservation` | | identifiant/descripteur provider opaques | PASS | `MarketPriceProviderId` + `MarketPriceProviderDescriptor` | | auth/rate-limit/quota représentables génériquement | PASS | types provider-neutral dédiés | | settings communs sans faux endpoint/API key | PASS | `MarketPriceProviderCommonSettings` | | availability provider-neutral représentable | PASS | `MarketPriceProviderAvailability` + `MarketPriceProviderState` | | HTTP actif ou adapter provider ajouté | N/A | explicitement réservé à `pre.003+` | ## 3.1 Taxonomie Off-chain et observabilité | Invariant | Statut | |--------------------------------------------------------------------------|--------| | crate `ksp-offchain-transport-lib` non assimilée à une crate price-only | PASS | | capacité V1 classée sous modules privés `market_price_*` | PASS | | modules partagés `constants.rs` et `error.rs` restent crate-wide | PASS | | futures quotes prévues sous famille distincte `swap_quote_*` | PASS | | aucune abstraction future créée sans responsabilité réellement partagée | PASS | | `market_price` V1 strictement read-only | PASS | | verbe HTTP distinct de la sémantique métier read/write | PASS | | soumission/signature Solana reste hors d'Off-chain Transport | PASS | | `ksp-logging-lib` utilisé pour les événements KSP de la crate | PASS | | `src/constants.rs` possède `TRACING_TARGET = ksp-offchain-transport-lib` | PASS | | aucune dépendance directe à `tracing` | PASS | | crate-rustdocs présentes dans les tests d'intégration | PASS | ## 3.2 Correctif `0.2.11-pre.002-fix.002` Le gate `cargo clippy --workspace --all-targets` de `pre.002-fix.001` a détecté un unique écart `clippy::implicit-return` dans le canari `tests/dependency_boundary.rs`. Le correctif ajoute le `return` explicite attendu dans la closure de `Iterator::any` et synchronise `workspace.package.version` sur `0.2.11-pre.2.fix.2`. Aucun contrat public, comportement runtime ou scope provider n'est modifié. Les validations opérateur du `2026-08-25` sur `pre.002-fix.002` sont : `cargo fmt --all` PASS, audit Rust PASS, audit Markdown PASS, `cargo check --workspace` PASS, `cargo test -p ksp-offchain-transport-lib` PASS avec 13 tests exécutés, et `cargo clippy --workspace --all-targets` PASS. `cargo test --workspace` n'a pas été rejoué après ce fix et n'est donc pas déclaré PASS pour cet état exact. ## 3.3 Correctif `0.2.11-pre.002-fix.003` Le nommage public initial `Price*` restait trop générique alors que les modules qui possèdent ces contrats sont explicitement la famille `market_price_*`. Le correctif synchronise `workspace.package.version` sur `0.2.11-pre.2.fix.3` et renomme sans alias de compatibilité toute la surface publique spécifique à cette famille en `MarketPrice*` / `MARKET_PRICE_*`. Le contrat attendu après application est : ```text MarketPriceDecimal MarketPriceObservation MarketPriceTimestamp MarketPriceProvenance MarketPricePair MarketPriceSemantics MarketPriceProvider* MARKET_PRICE_* ERROR_CODE_MARKET_PRICE_* ``` Cette correction reste dans la responsabilité de `pre.002` : elle stabilise la fondation et sa façade avant l'introduction du HTTP runtime en `pre.003`. ## 3.4 Gate `0.2.11-pre.003` | Critère | Statut | Preuve | |-----------------------------------------------------------------------|--------|-----------------------------------------------------| | version workspace `0.2.11-pre.3` | PASS | `Cargo.toml` racine | | `reqwest` utilisé directement, sans SDK provider | PASS | manifest `ksp-offchain-transport-lib` | | primitives HTTP communes sous modules `http_*` crate-private | PASS | `http_client`, `http_settings`, `http_admission` | | client HTTP générique non exporté aux consumers | PASS | façade `lib.rs` | | redirects, Referer, proxy système et retries reqwest désactivés | PASS | construction `HttpRestClient` | | connect/request timeouts bornés | PASS | `HttpClientSettings` | | body borné pendant lecture chunked avant désérialisation | PASS | `HttpRestClient::get_json` | | JSON syntaxiquement validé sans normalisation numérique intermédiaire | PASS | `serde::de::IgnoredAny` sur bytes bruts | | 401/403, 429, 408/5xx et autres statuts distingués | PASS | codes d'erreur HTTP dédiés | | URL reqwest supprimée des sources d'erreur | PASS | `reqwest::Error::without_url` | | body remote non recopié dans les erreurs | PASS | erreurs construites uniquement depuis metadata safe | | limiter fixe lissé et burst absent traité conservativement | PASS | `HttpAdmissionController` | | limite dynamique n'invente pas de cadence locale | PASS | `HttpAdmissionPolicy::Dynamic` | | cooldown `Retry-After` borné et admission non bloquante | PASS | `record_rate_limited` + `try_admit` | | adapters CoinGecko/Jupiter/etc. ajoutés | N/A | explicitement réservés à `pre.004+` | | gate Cargo `check`/`clippy`/tests Off-chain sur `pre.003-fix.001` | PASS | résultats opérateur du 2026-08-25 | | audit Rust final de `pre.003-fix.001` | CARRY | seul `RUST-FMT-108`, cause supprimée en `pre.004` | Les tests déterministes de `pre.003` couvrent en particulier le body chunked dépassant la limite, le refus des redirects, un `429` avec `Retry-After`, la non-propagation d'un body distant canari, le JSON invalide et le refill/cooldown du limiter. ## 3.5 Correctif `0.2.11-pre.003-fix.001` Le gate opérateur de `pre.003` a révélé quatre classes d'écarts avant poursuite vers `pre.004` : six violations `RUST-FMT-104` dans `src/error.rs`, six réexports `pub(crate)` HTTP inutilisés dans le build normal, un test de burst rejeté par la validation `burst > requests`, et la non-utilisation de la façade crate-root dans les `impl` des items HTTP partagés. `cargo check` et `cargo clippy` terminaient avec les warnings de réexports, tandis que `cargo test -p ksp-offchain-transport-lib` échouait sur `documented_burst_is_consumed_atomically_before_refill`. Le correctif applique les règles suivantes : ```text RUST-FMT-005 / RUST-FMT-104 : constantes homogènes triées alphabétiquement RUST-IMPORT-009 / RUST-API-004 : items HTTP partagés consommés via crate::Item, y compris dans leurs impl RUST-API-008 : helpers préparatoires sans consumer production sous #[cfg(test)], aucun #[allow(dead_code)] compensatoire contrat limiter : burst documenté indépendant de la cadence moyenne et autorisé à la dépasser ``` Le cas `1 req/s` avec `burst 2` devient un canari déterministe ; il protège notamment la représentabilité du cas Coinbase documenté dans le plan à `10 req/s, burst 15`. Les modules `http_*` restent donc testés et prêts en `pre.003`, mais leur compilation normale sera activée en `pre.004` lorsque les premiers adapters de production les consommeront réellement. La version Cargo du correctif est `0.2.11-pre.3.fix.1`. Le gate opérateur du `2026-08-25` confirme `cargo check --workspace` PASS, `cargo clippy --workspace --all-targets` PASS et `cargo test -p ksp-offchain-transport-lib` PASS avec 22 unitaires, 2 boundary et 2 public API. L'audit Rust signale uniquement `RUST-FMT-108` dans `src/lib.rs`, conséquence directe du staging `#[cfg(test)]` des modules `http_*`. L'opérateur a explicitement décidé de ne pas publier un fix artificiel pour ce point puisque `pre.004` retire ce staging avec les premiers consumers de production. ## 3.6 Gate `0.2.11-pre.004` | Critère | Statut | Preuve | |------------------------------------------------------------------------|--------|------------------------------------------------------------------------| | version workspace `0.2.11-pre.4` | PASS | `Cargo.toml` racine | | modules `http_*` actifs en production, sans staging crate-root test | PASS | `src/lib.rs` | | cause `RUST-FMT-108` de `pre.003-fix.001` supprimée | PASS | modules production avant tests | | CoinGecko SOL/USD adapter public | PASS | `market_price_coingecko.rs` | | CoinGecko keyless et Demo key distingués | PASS | settings + descriptor + header sensible | | CoinMarketCap SOL/USD adapter public | PASS | `market_price_coinmarketcap.rs` | | CoinMarketCap Simple Price V2 utilisé, V1 deprecated absent du runtime | PASS | endpoints V2 fixes + canari dependency boundary | | CoinMarketCap keyless et Basic key distingués | PASS | settings + descriptor + header sensible | | CoinPaprika SOL/USD adapter public | PASS | `market_price_coinpaprika.rs` | | CoinPaprika sans faux credential | PASS | settings keyless uniquement | | prix JSON provider parsé exactement sans passage par `f64` | PASS | `RawValue` -> `MarketPriceDecimal::parse_json_raw` | | timestamps provider conservés lorsqu'ils existent | PASS | Unix seconds CoinGecko, RFC3339 CoinMarketCap/CoinPaprika | | DTOs wire provider privés | PASS | structs privées dans chaque module provider | | endpoints provider fixes et HTTPS | PASS | constantes privées + `HttpGetRequest::new_https` | | API keys absentes de `Debug` et diagnostics | PASS | `MarketPriceApiKey` redacted + tests settings | | `429` alimente le cooldown local partagé | PASS | `get_json` -> `record_rate_limited` | | aucun SDK provider | PASS | manifest + boundary tests | | Config, registry global et refresh multiple non avancés | PASS | scopes réservés à `pre.007+` / `pre.009` | | audit Rust sandbox | PASS | clean, 0 candidate export | | audit Markdown sandbox | PASS | clean, 119 tables / 110 fichiers | | gate Cargo complet `pre.004` | FAIL | audit Rust : 1 x RUST-FMT-104 ; Clippy : 2 warnings ; 1 fixture CMC KO | Les tests déterministes de `pre.004` couvrent les modes d'auth, les headers sans fuite de credential, les URLs/query fixes, le parsing des réponses SOL/USD, le rejet d'identités ou schémas incohérents et le parsing exact des nombres JSON. Aucun smoke réseau n'est revendiqué dans cette tranche. ## 3.7 Correctif `0.2.11-pre.004-fix.001` Le gate opérateur du `2026-08-25` sur `pre.004` a confirmé `cargo check --workspace` PASS, mais a détecté trois écarts avant `pre.005` : une violation `RUST-FMT-104` dans le bloc de constantes de `src/error.rs`, deux warnings `clippy::collapsible_if` dans les builders CoinGecko/CoinMarketCap et un échec du test `coinmarketcap_v2_fixture_normalizes_string_status_and_exact_price`. L'échec CoinMarketCap provenait uniquement de la fixture : la concaténation produisait `...,151.987654321012345678,last_updated...` au lieu de `...,151.987654321012345678,"last_updated"...`. Le parser runtime n'est pas modifié. Le correctif remet le bloc de constantes dans l'ordre alphabétique complet, utilise des `let`-chains pour les deux insertions de headers optionnels et corrige le JSON déterministe de la fixture. La version Cargo du correctif est `0.2.11-pre.4.fix.1`. Aucun endpoint, mode d'auth, descriptor, limite provider ou contrat public n'est modifié. Le gate opérateur rejoué le `2026-08-25` est intégralement PASS : audit Rust clean avec 0 candidate export, audit Markdown clean à 118 tables / 109 fichiers, `cargo check --workspace` PASS, `cargo clippy --workspace --all-targets` PASS, `cargo test -p ksp-offchain-transport-lib` PASS avec 31 unitaires + 3 boundary + 3 public API, puis `cargo test --workspace` PASS avec uniquement les smokes/timing explicitement ignorés. ## 3.8 Gate `0.2.11-pre.005` | Critère | Statut | Preuve | |--------------------------------------------------------|--------|----------------------------------------------------| | version workspace `0.2.11-pre.5` | PASS | `Cargo.toml` racine | | Kraken Spot SOL/USD adapter public | PASS | `market_price_kraken.rs` | | Kraken public keyless sans faux credential | PASS | settings + descriptor | | Kraken dernier trade conservé avec sémantique exchange | PASS | ticker `SOLUSD` -> `c[0]` -> `ExchangeLastTrade` | | Kraken ne fabrique aucun timestamp provider absent | PASS | `provider_timestamp = None` | | Kraken cadence locale conservatrice 1 req/s IP | PASS | descriptor + canari unitaire | | Coinbase Exchange SOL/USD adapter public | PASS | `market_price_coinbase_exchange.rs` | | Coinbase Exchange public keyless sans faux credential | PASS | settings + descriptor | | Coinbase dernier trade et timestamp provider conservés | PASS | `price` + `time` -> `ExchangeLastTrade` | | Coinbase token bucket public 10 req/s IP, burst 15 | PASS | descriptor + canari unitaire | | DTOs wire Kraken/Coinbase privés | PASS | structs privées dans les modules providers | | endpoints provider fixes et HTTPS | PASS | constantes privées + boundary canary | | prix exchange parsés exactement sans passage par `f64` | PASS | `RawValue` -> `MarketPriceDecimal::parse_json_raw` | | aucun SDK provider, Config ou registry global avancé | PASS | manifest + dependency boundary | | audit Rust sandbox | PASS | clean, 0 candidate export | | audit Markdown sandbox | PASS | clean, 119 tables / 110 fichiers | | `cargo check` / Clippy / tests Off-chain | PASS | résultats opérateur du 2026-08-25 | | `cargo test --workspace` sur état exact `pre.005` | N/R | non rejoué avant passage explicite à `pre.006` | Les tests déterministes de `pre.005` couvrent les descriptors exchange, la précision décimale, le timestamp Coinbase, l'absence de timestamp Kraken, le rejet d'une erreur/identité Kraken incohérente et le rejet d'un timestamp Coinbase invalide. Le gate opérateur ciblé du `2026-08-25` est PASS : audit Rust clean, audit Markdown clean à 119 tables / 110 fichiers, `cargo check --workspace` PASS, `cargo clippy --workspace --all-targets` PASS et `cargo test -p ksp-offchain-transport-lib` PASS avec 37 unitaires + 3 boundary + 4 public API. `cargo test --workspace` n'a pas été rejoué sur cet état exact. ## 3.9 Gate `0.2.11-pre.006` | Critère | Statut | Preuve | |-----------------------------------------------------------------|--------|---------------------------------------------------------| | version workspace `0.2.11-pre.6` | PASS | `Cargo.toml` racine | | Jupiter Price V3 adapter public | PASS | `market_price_jupiter.rs` | | Jupiter keyless et Free key distingués | PASS | settings + descriptor + header sensible | | Jupiter keyless 0,5 RPS et Free 1 RPS représentés | PASS | descriptors + canaries unitaires | | Jupiter utilise uniquement le mint WSOL | PASS | query `ids` fixe | | `usdPrice` Jupiter parsé exactement sans `f64` canonique | PASS | `RawValue` -> `MarketPriceDecimal` | | `blockId` Jupiter conservé en provenance sans faux timestamp | PASS | provenance bornée + `provider_timestamp = None` | | DexScreener adapter public avec pair address explicite | PASS | `MarketPriceDexScreenerSettings` | | DexScreener endpoint direct pair uniquement | PASS | `/latest/dex/pairs/solana/{pairId}` | | DexScreener vérifie `chainId`, pair address et identité WSOL | PASS | parser provider | | DexScreener utilise uniquement `priceUsd` | PASS | DTO wire privé + parsing exact | | DexScreener 300 req/min représenté | PASS | descriptor provider | | aucune découverte/selection/ranking automatique de pool | PASS | dependency boundary + absence search/token-pairs/tokens | | aucun SDK provider, Config, registry ou refresh multiple avancé | PASS | manifest + scope des modules | | audit Rust sandbox | PASS | clean, 0 candidate export | | audit Markdown sandbox | PASS | clean, 120 tables / 111 fichiers | | `cargo check` / Clippy / tests Off-chain | PASS | résultats opérateur du 2026-08-25 | | `cargo test --workspace` sur état exact `pre.006` | N/R | non rejoué avant passage explicite à `pre.007` | Les fixtures de `pre.006` couvrent Jupiter keyless/Free, redaction de la clé, prix exact, identité WSOL, `blockId`, absence volontaire de faux timestamp, ainsi que DexScreener avec pair Solana explicite, `priceUsd`, rejet de chain/pair/base invalides, réponse multiple et prix absent. Le gate opérateur ciblé du `2026-08-25` est PASS : audit Rust clean, audit Markdown clean à 120 tables / 111 fichiers, `cargo check --workspace` PASS, `cargo clippy --workspace --all-targets` PASS et `cargo test -p ksp-offchain-transport-lib` PASS avec 43 unitaires + 4 boundary + 5 public API. `cargo test --workspace` n'a pas été rejoué sur cet état exact. ## 3.10 Gate `0.2.11-pre.007` | Critère | Statut | Preuve | |------------------------------------------------------------------|--------|---------------------------------------| | version workspace `0.2.11-pre.7` | PASS | `Cargo.toml` racine | | Birdeye Standard SOL/USD adapter public | PASS | `market_price_birdeye.rs` | | Birdeye exige `X-API-KEY` lorsqu'activé | PASS | settings + header sensible | | Birdeye fixe `x-chain = solana` et le mint WSOL | PASS | request builder | | Birdeye Price Single `value` parsé exactement | PASS | `RawValue` -> `MarketPriceDecimal` | | Birdeye `updateUnixTime` conservé comme timestamp provider | PASS | parser provider | | Birdeye Standard 1 RPS compte modélisé | PASS | descriptor provider | | quota Birdeye 30 000 compute units informatif | PASS | `MarketPriceProviderLongTermQuota` | | coût Price Single 3 compute units informatif | PASS | `MarketPriceProviderRequestCost` | | registry provider-neutral matérialisé | PASS | `MarketPriceProviderRegistry` | | huit providers V1 présents dans le canari de complétude registry | PASS | test `market_price_registry` | | registry trié par provider id et doublons rejetés | PASS | constructeur public générique + tests | | entry publique limitée à descriptor + état générique | PASS | `MarketPriceProviderRegistryEntry` | | availability expose éligibilité et `retry_at` génériques | PASS | `MarketPriceProviderAvailability` | | registry sans mutation ou dispatch réseau | PASS | projection générique publique | | registry ne contient aucun dispatch/name provider | PASS | dependency boundary | | refresh single/multiple, Config et smoke live non avancés | PASS | scope réservé à `pre.008+` | | audit Rust sandbox | PASS | clean, 0 candidate export | | audit Markdown sandbox | PASS | clean, 121 tables / 112 fichiers | | gate Cargo | PASS | gate opérateur complet du 2026-08-26 | Les tests déterministes de `pre.007` couvrent l'auth Birdeye et sa redaction, le descriptor 1 RPS / 30 000 CU / 3 CU, le parsing exact du prix et du timestamp, les réponses invalides, ainsi que le registry complet des huit providers, son ordre déterministe, le rejet de doublons et les projections d'availability. Le gate opérateur du 2026-08-26 est intégralement PASS : audits Rust/Markdown, `cargo check --workspace`, `cargo clippy --workspace --all-targets`, tests ciblés Off-chain et `cargo test --workspace`. Aucun smoke live provider n'est revendiqué. ## 3.11 Gate `0.2.11-pre.008` | Critère | Statut | Preuve | |-------------------------------------------------------------------------|--------|--------------------------------------------| | version workspace `0.2.11-pre.8` | PASS | `Cargo.toml` racine | | `MarketPriceService` provider-agnostic public | PASS | `market_price_service.rs` | | setup provider-specific limité à la composition | PASS | `MarketPriceProviderSetup` | | registry détachable sans dispatch provider | PASS | `MarketPriceService::registry` | | refresh individuel par `MarketPriceProviderId` | PASS | `MarketPriceService::refresh` | | refresh multiple par liste d'identifiants opaques | PASS | `MarketPriceService::refresh_many` | | refresh all en ordre provider déterministe | PASS | `MarketPriceService::refresh_all` | | doublons / provider inconnu rejetés avant dispatch | PASS | tests `market_price_service` | | provider non éligible projeté sans requête réseau | PASS | eligibility + test huit providers disabled | | `429` / defer local projetés en `CoolingDown` | PASS | classification générique | | `401/403` keyed projetés en `AuthenticationUnavailable` | PASS | classification auth-aware | | transport temporaire projeté sans rendre le service global inutilisable | PASS | outcome provider-neutral | | aucune attente/sleep de rate limit dans le service | PASS | dependency boundary | | aucun fallback, consensus ou agrégation multi-provider | PASS | scope + dependency boundary | | Config, scheduling périodique et smoke live non avancés | PASS | scope réservé à `pre.009+` | | audit Rust sandbox | PASS | clean, 0 candidate export | | audit Markdown sandbox | PASS | clean, 122 tables / 113 fichiers | | gate `pre.008` brut | FAIL | 1 warning Clippy + 1 canari boundary FAIL | | fix `pre.008-fix.001` statique | PASS | audits Rust/Markdown sandbox | | gate Cargo du fix | PASS | gate opérateur complet du 2026-08-26 | Les tests déterministes de `pre.008` vérifient le registry de service sur les huit setups V1, `refresh_all` sans réseau lorsque tous les providers sont désactivés, le rejet pré-dispatch des doublons/providers inconnus, les classes auth/rate-limit/temporaire/local misconfiguration et l'éligibilité temporelle des cooldowns. Les méthodes multiples restent séquentielles et déterministes dans V1 ; elles ne dorment jamais pour attendre un provider et ne font aucun fallback/consensus. Le gate opérateur initial a toutefois trouvé un warning `clippy::collapsible_if` et un canari boundary trop large (`!service.contains("consensus")`) qui échouait sur la rustdoc décrivant justement l'absence de consensus. `pre.008-fix.001` corrige ces deux points sans modifier le comportement runtime. Le gate rejoué sur `0.2.11-pre.8.fix.1` est intégralement PASS : audits Rust/Markdown, `cargo check --workspace`, `cargo clippy --workspace --all-targets`, 54 tests unitaires + 6 boundary + 7 public API Off-chain, puis `cargo test --workspace` avec uniquement les smokes/timing explicitement ignorés. ## 3.12 Gate `0.2.11-pre.009` | Critère | Statut | Preuve | |----------------------------------------------------------------------|--------|-----------------------------------------------------| | version workspace `0.2.11-pre.9` | PASS | `Cargo.toml` racine | | document `cfg.std.offchain_transport` enregistré | PASS | registry Config + `std.offchain_transport.json` | | schema `schema.std.offchain_transport` enregistré | PASS | registry Config + schema Draft 2020-12 | | domaine actuel imbriqué sous `market_price` | PASS | document/schema V1 | | profils `public_keyless` et `all_free` versionnés | PASS | document standard + exemple | | huit branches provider Config obligatoires | PASS | schema + fixture | | credentials conditionnés par mode/provider | PASS | `if/then` du schema | | credentials effectifs exigent une provenance Config Secret | PASS | adapter `offchain_transport.rs` + tests | | littéral credential rejeté par l'adapter effectif | PASS | test `literal_or_nonsecret_provider_credentials...` | | DexScreener pair requis seulement lorsque le provider est activé | PASS | schema + `MarketPriceDexScreenerSettings` | | Dex pair via environnement exige une provenance Public | PASS | adapter Config + test | | `.env.example` inventorie quatre secrets et la paire publique | PASS | `.env.example` | | Config construit `MarketPriceService` via `MarketPriceProviderSetup` | PASS | `ResolvedOffchainTransportConfig` | | Off-chain Transport ne dépend pas de Config | PASS | dependency ownership + manifest | | aucune cadence provider libre ajoutée à Config | PASS | schema/document sans rate-limit override | | Config Debug/safe view ne divulgue pas les API keys | PASS | tests adapter | | applications desktop embarquent document + schema | PASS | deux `tauri.conf.json` + canaries desktop | | audit Rust sandbox | PASS | clean, 0 candidate export | | audit Markdown sandbox | PASS | clean, 123 tables / 115 fichiers | | `cargo check` / Clippy / tests Config et workspace | PASS | gate opérateur complet du 2026-08-26 | La tranche n'ajoute aucune URL provider configurable, aucun SDK provider et aucune lecture directe de l'environnement dans `ksp-offchain-transport-lib`. Les secrets restent résolus par `ksp-config-lib`; le service runtime reçoit uniquement les settings typés nécessaires. La modification DexScreener est capability-aware : une paire absente est valide uniquement lorsque l'adapter est désactivé, ce qui permet de conserver son entrée dans l'inventaire générique sans fabriquer de fausse configuration. Le gate opérateur de `pre.009` est intégralement PASS : audits Rust/Markdown, `cargo check --workspace`, `cargo clippy --workspace --all-targets`, tests ciblés Off-chain, 119 tests unitaires Config et `cargo test --workspace`. Seuls les smokes live et probes de timing explicitement `ignored` restent non exécutés par le gate déterministe. ## 3.13 Gate `0.2.11-pre.010` | Critère | Statut | Preuve | |------------------------------------------------------------------------|--------|-----------------------------------------------------| | version workspace `0.2.11-pre.10.fix.1` | PASS | `Cargo.toml` racine | | enums publiques évolutives marquées `#[non_exhaustive]` | PASS | provider/access modes/setup + canari completeness | | adapter Config future-safe face aux access modes non exhaustifs | PASS | branches wildcard vers erreur effective sûre | | inventaire exact des huit providers et sémantiques verrouillé | PASS | `tests/release_completeness.rs` | | aucun environnement lu par les modules `market_price_*` | PASS | `tests/security_hardening.rs` | | aucun `as_f64` utilisé comme vérité de prix `market_price` | PASS | `tests/security_hardening.rs` | | aucun bypass logging direct dans le workspace | PASS | scanner global `ksp-logging-lib/tests/ownership.rs` | | Debug keyed settings/service ne divulgue pas les API keys | PASS | canari secret déterministe | | Config rejette URL provider et override de rate limit | PASS | schema strict + test Config adversarial | | timestamps RFC3339/pre-epoch/overflow durcis | PASS | unit test `market_price_adapter` | | JSON numeric pathologique/null/non-numérique rejeté | PASS | unit test `market_price_decimal` | | provenance accepte la borne et rejette trim/control/oversize | PASS | unit test `market_price_observation` | | `refresh_many` / `refresh_all` V1 documentés séquentiels/déterministes | PASS | plan + `README.md` / `USAGE.md` Off-chain | | README/USAGE techniques créés sans usurper la réconciliation finale | PASS | crate Off-chain + compléments Config | | audit Rust sandbox | PASS | clean, 0 candidate export | | audit Markdown sandbox | PASS | clean sur le fix | | gate brut `pre.010` | FAIL | workspace ownership logging : 1 canari FAIL | | fix `pre.010-fix.001` statique | PASS | doublon lexical retiré, audits sandbox | | gate Cargo complet du fix | PASS | gate opérateur complet du 2026-08-26 | `pre.010` ne fait aucun smoke live, ne change aucun endpoint provider et n'introduit ni fallback, ni consensus, ni scheduler. Les documents crate créés ici sont des brouillons techniques courants ; `pre.012` reste seule propriétaire de la réconciliation documentaire finale après le gate technique/live de `pre.011`. Le gate brut de `pre.010` a passé audits, `cargo check`, Clippy et tests ciblés, puis `cargo test --workspace` a échoué dans `ksp-logging-lib/tests/ownership.rs` parce que `tests/security_hardening.rs` contenait littéralement le chemin logging interdit dans une assertion destinée à le bannir. `pre.010-fix.001` supprime ce scanner local redondant et conserve `ksp-logging-lib/tests/ownership.rs` comme autorité workspace unique. Le gate rejoué sur `0.2.11-pre.10.fix.1` est ensuite intégralement PASS : audits Rust/Markdown, `cargo check --workspace`, Clippy, 58 unitaires + 6 boundary + 7 public API + 2 completeness + 3 hardening Off-chain, 120 unitaires Config et `cargo test --workspace`. ## 3.14 Gate `0.2.11-pre.011` | Critère | Statut | Preuve / condition opérateur | |--------------------------------------------------------------|---------|---------------------------------------------------------------| | version workspace `0.2.11-pre.11` | PASS | `Cargo.toml` racine | | réaudit officiel des huit providers au `2026-08-26` | PASS | sources provider courantes, aucun contrat wire à modifier | | CoinPaprika fraîcheur contradictoire sans faux scheduler | PASS | référence ~5 min / plans jusqu’à 10 min, runtime conservateur | | smoke keyless durable sur sept providers | PASS | test ignoré `keyless_market_price_providers_live_smoke` | | smoke keyed durable sur quatre modes gratuits | PASS | test ignoré `keyed_market_price_providers_live_smoke` | | secrets keyed lus sur `stdin`, jamais en argument CLI | PASS | `tests/market_price_live_smoke.rs` | | DexScreener utilise une paire publique explicite sans search | PASS | Orca SOL/USDC canari, WSOL base | | un échec provider ne court-circuite pas le batch live | PASS | outcomes collectés avant assertion finale | | aucun prix exact live figé | PASS | validation identité/pair/sémantique/prix positif uniquement | | audit Rust sandbox | PASS | clean, 0 candidate export | | audit Markdown sandbox | PASS | clean, 125 tables / 120 fichiers | | gate Cargo déterministe complet | PASS | opérateur, workspace complet | | `cargo tree` Off-chain | PASS | opérateur, aucun SDK provider / aucune dépendance Config | | smoke keyless réel | FAIL | 6/7 PASS, CoinMarketCap seul échec avant fix | | smoke keyed réel | PENDING | exécuter si les quatre credentials gratuits sont disponibles | La tranche `pre.011` ne change aucune logique réseau de production. Elle rend uniquement le gate live durable et réaudite les contrats externes avant stable. Le smoke keyless est le gate live sans credential obligatoire. Le smoke keyed peut être `SKIP opérateur` si les quatre clés ne sont pas disponibles ; ce statut doit être consigné explicitement dans la validation finale et ne doit jamais être transformé en PASS fictif. ## 3.15 Correctif `0.2.11-pre.011-fix.001` Le smoke keyless opérateur de `pre.011` a bien exécuté les sept providers avant assertion finale. Les six adapters Coinbase Exchange, CoinGecko, CoinPaprika, DexScreener, Jupiter et Kraken ont produit une observation valide. CoinMarketCap seul a terminé `TemporarilyUnavailable`. Le réaudit officiel ciblé a identifié un écart déterministe dans le builder KSP : Simple Price V2 attend `id=5426`, alors que l'adapter envoyait `ids=5426`. | Critère | Statut | Preuve / condition opérateur | |----------------------------------------------------|--------|-----------------------------------------------------| | version workspace `0.2.11-pre.11.fix.1` | PASS | `Cargo.toml` racine | | endpoint CoinMarketCap V2 inchangé | PASS | `/v2/simple/price` et `/public-api/v2/simple/price` | | identifiant SOL CoinMarketCap corrigé en `id=5426` | PASS | builder commun Keyless / Basic | | query V2 exacte canarisée | PASS | `id=5426&convert=USD&include_last_updated=true` | | parser/rate-limit/auth/sémantique inchangés | PASS | aucun autre contrat runtime modifié | | six autres providers keyless du smoke initial | PASS | outcomes live `pre.011`, aucun autre échec | | re-smoke keyless après fix | PASS | 7/7 opérateur le 2026-08-26 | | smoke keyed réel | SKIP | opérateur sans les quatre credentials gratuits | Ce correctif est technique et appartient toujours à la lane `pre.011`. Il ne transfère aucune responsabilité documentaire à `pre.012`. ## 3.16 Réconciliation documentaire `0.2.11-pre.012` La lane technique/live est fermée avant cette tranche : le premier smoke keyless a isolé CoinMarketCap, `pre.011-fix.001` a corrigé le paramètre V2, puis le re-smoke opérateur a passé les sept providers keyless en `7/7`. Le smoke keyed n'a pas été exécuté faute des quatre credentials gratuits simultanés et reste explicitement `SKIP opérateur`. `pre.012` ne modifie aucun comportement Rust, test, schema, Config exécutable, dépendance ou contrat réseau. La rustdoc crate-root est seulement réconciliée pour retirer `pre.010`. La tranche prépare ensuite un dernier gate workspace sans refaire le smoke live ni les graphes Cargo. | Critère | Statut | Preuve / condition opérateur | |--------------------------------------------------------|---------|-------------------------------------------------| | re-smoke keyless après CoinMarketCap fix | PASS | 7/7 le 2026-08-26 | | smoke keyed avec quatre credentials | SKIP | credentials non disponibles ensemble | | plan `0.2.11` réconcilié | PASS | statuts techniques/live finalisés | | matrice provider finale | PASS | huit providers implémentés | | README/USAGE Off-chain durables | PASS | brouillon `pre.010` supprimé | | index documentaire réconcilié | PASS | `docs/000-README.md` | | séquence fonctionnelle `0.2.11` / `0.2.12` réconciliée | PASS | `002-FUNCTIONAL_RELEASE_SEQUENCE.md` | | aucune responsabilité `CHANGELOG` / `ROADMAP` / prompt | PASS | réservée à `pre.013` | | gate workspace documentaire final | PENDING | opérateur après application exacte de `pre.012` | ## 4. Matrice provider finale | Provider | SOL/USD V1 | Gratuit V1 | Mode auth prévu | Test déterministe | Smoke live | Statut courant | |-------------------|------------|------------|----------------------|-------------------|--------------|----------------| | CoinGecko | Oui | Oui | keyless ou Demo key | PASS | PASS keyless | IMPLEMENTED | | CoinMarketCap | Oui | Oui | keyless ou Basic key | PASS | PASS keyless | IMPLEMENTED | | CoinPaprika | Oui | Oui | aucune | PASS | PASS | IMPLEMENTED | | Kraken | Oui | Oui | aucune | PASS | PASS | IMPLEMENTED | | Coinbase Exchange | Oui | Oui | aucune | PASS | PASS | IMPLEMENTED | | Jupiter Price V3 | Oui | Oui | keyless ou Free key | PASS | PASS keyless | IMPLEMENTED | | Birdeye | Oui | Oui | API key Standard | PASS | SKIP keyed | IMPLEMENTED | | DexScreener | Oui | Oui | aucune | PASS | PASS | IMPLEMENTED | Le smoke `PASS` signifie qu'un mode keyless de ce provider a été réellement exercé par le harness KSP de clôture. `SKIP keyed` signifie uniquement que Birdeye, qui n'a pas de mode keyless V1, n'a pas reçu de preuve live faute du jeu complet de credentials opérateur ; ses fixtures, parsing, auth et descriptors restent couverts déterministiquement. Les conditions d'usage et la persistance des offres gratuites ont été réauditées au gate final du `2026-08-26`. Elles restent des propriétés externes susceptibles d'évoluer après la release et ne constituent pas une garantie commerciale KSP. ## 5. Contrat public final | Invariant | Statut | |------------------------------------------------------------------------|--------| | surface publique limitée à SOL/USD dans V1 | PASS | | identifiants provider opaques pour les consumers | PASS | | provenance provider toujours observable | PASS | | sémantique de prix observable sans prétendre à une équivalence | PASS | | timestamp requête/réception KSP présents | PASS | | timestamp provider optionnel seulement lorsqu'il est réellement fourni | PASS | | absence de prix distincte de zéro | PASS | | `f64` non utilisé comme canon public | PASS | | aucune structure wire provider exportée | PASS | | aucune URL/header/asset id provider requis côté app | PASS | | registry provider et état runtime possédés par Off-chain Transport | PASS | | refresh provider par identifiant générique | PASS | | refresh multiple sans connaissance provider côté consumer | PASS | ## 6. Rate limiting et availability | Cas | Statut | |---------------------------------------------------------------|--------| | cadence locale fixe représentable | PASS | | burst documenté supérieur à la cadence moyenne représentable | PASS | | limite dynamique keyless représentable | PASS | | `429` classé | PASS | | `Retry-After` honoré lorsqu'exploitable | PASS | | cooldown expose prochain instant admissible | PASS | | refresh all ne lance que les providers éligibles | PASS | | provider en cooldown ne bloque pas les autres | PASS | | provider down ne rend pas le service global inutilisable | PASS | | auth rejetée distinguée d'un transport down | PASS | | état `QuotaUnavailable` distinct représentable | PASS | | quota mensuel local non présenté comme compteur authoritative | PASS | ## 7. Numeric safety | Cas | Statut | |-----------------------------------------------------|--------| | décimal string valide | PASS | | JSON number valide sans passage canonique par `f64` | PASS | | notation scientifique bornée | PASS | | coefficient/scale overflow rejeté | PASS | | valeur négative rejetée | PASS | | zéro rejeté pour une observation réussie | PASS | | NaN/Inf impossibles dans le type canonique | PASS | | serialization canonique sans perte | PASS | | round-trip public déterministe | PASS | ## 8. HTTP et sécurité | Invariant | Statut | |------------------------------------------------|--------| | `reqwest` seul client provider | PASS | | aucun SDK provider | PASS | | endpoint provider officiel fixe en V1 | PASS | | redirects désactivés | PASS | | proxy implicite désactivé selon policy KSP | PASS | | connect timeout borné | PASS | | request timeout borné | PASS | | body réponse borné avant parsing | PASS | | URL retirée des erreurs reqwest | PASS | | réponse remote brute absente des KspError/logs | PASS | | API key absente de Debug/log/error | PASS | | 401/403 classés auth/access | PASS | | 429 classé rate limit | PASS | | 5xx classé transient | PASS | | schema drift classé réponse provider invalide | PASS | ## 9. DexScreener V1 | Invariant | Statut | |--------------------------------------------------|--------| | pair address SOL/USD explicite si provider actif | PASS | | `chainId = solana` vérifié | PASS | | identité SOL de la paire vérifiée | PASS | | `priceUsd` utilisé comme prix commun | PASS | | aucune recherche automatique de pool | PASS | | aucun tri automatique par liquidité | PASS | | aucune moyenne/consensus entre pools | PASS | ## 10. Config -> Off-chain Transport | Invariant | Statut | |------------------------------------------------------------------|--------| | Off-chain Transport ne dépend pas de Config | PASS | | document standard offchain dédié | PASS | | branches provider conditionnelles dans schema | PASS | | CoinGecko keyless/Demo représentés | PASS | | CoinMarketCap keyless/Basic représentés | PASS | | Jupiter keyless/Free représentés | PASS | | Birdeye exige credential lorsqu'activé | PASS | | DexScreener exige pair address lorsqu'activé | PASS | | providers sans auth n'exposent pas de faux champ API key | PASS | | secrets via Config uniquement | PASS | | `.env.example` synchronisé pour les secrets réellement ajoutés | PASS | | aucune cadence provider configurable au-dessus de la limite sûre | PASS | ## 11. Frontière future `ksp-app-solprices-desk` | Invariant | Statut | |--------------------------------------------------------------------|---------| | nom de l'application fixé à `ksp-app-solprices-desk` | DECIDED | | rôle HID uniquement | DECIDED | | aucun enum/provider spécifique requis dans l'UI | PLANNED | | aucun endpoint/header/provider id propriétaire dans l'app | PLANNED | | table construite depuis l'inventaire générique Off-chain Transport | PLANNED | | refresh row appelle une opération générique | PLANNED | | refresh multiple appelle une opération générique | PLANNED | | cooldown/availability affichables depuis état générique | PLANNED | | aucune logique de rate limiting dans Tauri/frontend | PLANNED | Cette section valide l'architecture préparée par `0.2.11`; l'application elle-même reste hors scope et sera testée en `0.2.12`. ## 12. Non-régression et gates finaux | Gate | Statut | |----------------------------------------------|---------| | `cargo fmt --all` | PASS | | audit Rust workspace | PASS | | audit Markdown tables | PASS | | `cargo check --workspace` | PASS | | `cargo clippy --workspace --all-targets` | PASS | | `cargo test --workspace` sur `pre.011` | PASS | | tests `ksp-offchain-transport-lib` après fix | PASS | | tests Config après fix | PASS | | cargo tree Off-chain Transport | PASS | | absence de dépendances provider SDK | PASS | | smoke keyless gratuit final | PASS | | smoke keyed quatre credentials | SKIP | | réconciliation README/USAGE/plan/validation | PASS | | gate workspace de `pre.012` | PENDING | ## 13. Hors scope validé ```text SOL/EUR et autres quotes SPL token price générique OHLCV/historique streaming price fallback automatique consensus/moyenne/médiane pool discovery DexScreener Price Desk dans 0.2.11 Wallet Desk dans 0.2.11 Pyth V1 Binance SOL/USDT assimilé à SOL/USD ```