v0.2.10-pre.002
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# file: Cargo.toml
|
||||
# version: 262
|
||||
# version: 263
|
||||
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["crates/ksp-app-config-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.2.10-pre.1"
|
||||
version = "0.2.10-pre.2"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
||||
|
||||
@@ -79,6 +79,59 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"profile_id": "orbitflare_devnet",
|
||||
"endpoints": [
|
||||
{
|
||||
"name": "solana_devnet_public",
|
||||
"enabled": true,
|
||||
"provider": "solana-public",
|
||||
"cluster": "devnet",
|
||||
"url": "${KSP_PUBLIC_SOLANA_DEVNET_HTTP_URL:-https://api.devnet.solana.com}",
|
||||
"connect_timeout_ms": 5000,
|
||||
"request_timeout_ms": 15000,
|
||||
"max_idle_connections_per_host": 8,
|
||||
"roles": [
|
||||
{
|
||||
"role": "default",
|
||||
"enabled": true,
|
||||
"request_kinds": [
|
||||
"*"
|
||||
],
|
||||
"priority": 100,
|
||||
"limits": {
|
||||
"requests_per_second": 5,
|
||||
"burst_capacity": 10,
|
||||
"max_concurrent_requests": 8,
|
||||
"pause_after_rate_limit_ms": 1000
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"ws_endpoints": [
|
||||
{
|
||||
"name": "solana_devnet_public_ws",
|
||||
"enabled": true,
|
||||
"provider": "solana-public",
|
||||
"cluster": "devnet",
|
||||
"kind": "solana_standard",
|
||||
"url": "${KSP_PUBLIC_SOLANA_DEVNET_WS_URL:-wss://api.devnet.solana.com}"
|
||||
}
|
||||
],
|
||||
"grpc_endpoints": [
|
||||
{
|
||||
"name": "orbitflare_solana_devnet_yellowstone",
|
||||
"enabled": true,
|
||||
"provider": "orbitflare",
|
||||
"cluster": "devnet",
|
||||
"protocol": "solana_yellowstone",
|
||||
"url": "http://devnet.rpc.orbitflare.com:10000",
|
||||
"metadata": [],
|
||||
"secret_metadata": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"profile_id": "mainnet_public",
|
||||
"endpoints": [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-config-lib/unit_tests/transport.rs
|
||||
// version: 10
|
||||
// version: 11
|
||||
|
||||
#[test]
|
||||
fn fixture_transport_profile_maps_complete_runtime_contract() {
|
||||
@@ -133,6 +133,45 @@ fn committed_transport_document_maps_default_and_explicit_profiles() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn committed_v3_orbitflare_devnet_profile_maps_provider_neutral_yellowstone_grpc_without_metadata() {
|
||||
let engine = committed_engine();
|
||||
let engine = match engine {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
||||
let resolved = engine.load_resolved_transport_config(std::option::Option::Some("orbitflare_devnet"), &environment);
|
||||
assert!(resolved.is_ok(), "committed OrbitFlare Devnet Transport profile should map without gRPC metadata: {resolved:?}");
|
||||
if let std::result::Result::Ok(resolved) = resolved {
|
||||
assert_eq!(resolved.profile_id(), "orbitflare_devnet");
|
||||
assert_eq!(resolved.selection_source(), crate::ConfigProfileSelectionSource::Explicit);
|
||||
assert_eq!(resolved.settings().endpoints()[0].cluster().as_str(), "devnet");
|
||||
assert_eq!(resolved.settings().endpoints()[0].url().as_str(), "https://api.devnet.solana.com");
|
||||
let ws = resolved.ws_settings();
|
||||
assert!(ws.is_some(), "OrbitFlare Devnet profile should retain the standard Solana Devnet WebSocket companion endpoint");
|
||||
if let std::option::Option::Some(ws) = ws {
|
||||
assert_eq!(ws.endpoints()[0].url().as_str(), "wss://api.devnet.solana.com");
|
||||
assert_eq!(ws.endpoints()[0].protocol(), ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard);
|
||||
}
|
||||
let grpc = resolved.grpc_settings();
|
||||
assert!(grpc.is_some(), "OrbitFlare Devnet profile should expose one provider-neutral Yellowstone gRPC endpoint");
|
||||
if let std::option::Option::Some(grpc) = grpc {
|
||||
assert_eq!(grpc.endpoints().len(), 1);
|
||||
let endpoint = &grpc.endpoints()[0];
|
||||
assert_eq!(endpoint.name(), "orbitflare_solana_devnet_yellowstone");
|
||||
assert_eq!(endpoint.provider().as_str(), "orbitflare");
|
||||
assert_eq!(endpoint.cluster().as_str(), "devnet");
|
||||
assert_eq!(endpoint.url().as_str(), "http://devnet.rpc.orbitflare.com:10000");
|
||||
assert!(!endpoint.url().uses_tls(), "OrbitFlare shared Devnet URL must remain explicitly HTTP/2 plaintext");
|
||||
assert!(endpoint.metadata().is_empty(), "OrbitFlare Devnet Free profile must not invent gRPC metadata before live proof requires it");
|
||||
let debug = format!("{endpoint:?}");
|
||||
assert!(!debug.contains("orbitflare.com"), "Transport settings Debug must not expose OrbitFlare gRPC endpoint URLs");
|
||||
assert!(!debug.contains("X-ORBIT-KEY"), "OrbitFlare Customer API credentials must never enter Yellowstone Transport settings");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn committed_v3_publicnode_profiles_map_provider_neutral_yellowstone_grpc_with_network_scoped_secret_x_tokens() {
|
||||
let engine = committed_engine();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-onchain-transport-lib/README.md -->
|
||||
<!-- version: 22 -->
|
||||
<!-- version: 23 -->
|
||||
|
||||
# `ksp-onchain-transport-lib`
|
||||
|
||||
@@ -220,7 +220,6 @@ Pour Helius, l’actor envoie automatiquement un control frame WebSocket `Ping`
|
||||
|
||||
LaserStream **gRPC** reste un backend distinct, hors de cette façade, de `WsProtocolKind` et de la Config WebSocket `helius_laserstream`.
|
||||
|
||||
|
||||
## Yellowstone gRPC standard
|
||||
|
||||
La foundation `0.2.9` ajoute un troisième backend réseau distinct de HTTP et WebSocket. Le moteur est KSP-owned : `yellowstone-grpc-proto` fournit le wire publié, tandis que Tonic reste encapsulé derrière les types crate-root KSP. Aucun client Tonic brut ni type protobuf upstream n’est réexporté.
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
// file: crates/ksp-onchain-transport-lib/tests/yellowstone_orbitflare_smoke.rs
|
||||
// version: 1
|
||||
|
||||
//! Opt-in live OrbitFlare Devnet characterization smoke for provider-neutral Yellowstone gRPC Subscribe.
|
||||
|
||||
fn orbitflare_devnet_endpoint() -> ksp_core_lib::Result<ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings> {
|
||||
let url = match ksp_onchain_transport_lib::YellowstoneGrpcEndpointUrl::parse("http://devnet.rpc.orbitflare.com:10000") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let session = ksp_onchain_transport_lib::YellowstoneGrpcSessionSettings::new(
|
||||
std::time::Duration::from_secs(10),
|
||||
std::time::Duration::from_secs(10),
|
||||
std::time::Duration::from_secs(5),
|
||||
ksp_onchain_transport_lib::YellowstoneGrpcReconnectSettings::new(0, std::time::Duration::from_millis(250), std::time::Duration::from_secs(2)),
|
||||
8,
|
||||
8,
|
||||
16 * 1024 * 1024,
|
||||
4 * 1024 * 1024,
|
||||
);
|
||||
return std::result::Result::Ok(ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings::new(
|
||||
"orbitflare_devnet_yellowstone",
|
||||
true,
|
||||
ksp_onchain_transport_lib::YellowstoneGrpcProviderName::new("orbitflare"),
|
||||
ksp_onchain_transport_lib::YellowstoneGrpcClusterName::new("devnet"),
|
||||
url,
|
||||
session,
|
||||
));
|
||||
}
|
||||
|
||||
fn slot_request() -> ksp_core_lib::Result<ksp_onchain_transport_lib::YellowstoneSubscribeRequest> {
|
||||
let mut request = ksp_onchain_transport_lib::YellowstoneSubscribeRequest::new();
|
||||
let name = match ksp_onchain_transport_lib::YellowstoneSubscribeFilterName::new("slots") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
match request.insert_slot_filter(name, ksp_onchain_transport_lib::YellowstoneSubscribeSlotFilter::new()) {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
request.set_commitment(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed));
|
||||
return std::result::Result::Ok(request);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
#[ignore = "opt-in live OrbitFlare Devnet Yellowstone gRPC characterization; connects without metadata, waits for Slot plus standard server Ping, then closes"]
|
||||
async fn orbitflare_devnet_yellowstone_streams_slots_and_emits_standard_server_ping_without_metadata() {
|
||||
let endpoint = match orbitflare_devnet_endpoint() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("programmatic OrbitFlare Devnet Yellowstone settings must be valid without metadata: {error:?}"),
|
||||
};
|
||||
assert!(endpoint.metadata().is_empty(), "OrbitFlare Devnet characterization must not invent gRPC metadata");
|
||||
assert!(!endpoint.url().uses_tls(), "the documented OrbitFlare shared Devnet endpoint is intentionally HTTP/2 plaintext");
|
||||
let endpoint_debug = format!("{endpoint:?}");
|
||||
assert!(!endpoint_debug.contains("orbitflare.com"), "OrbitFlare endpoint URL must not appear in endpoint Debug");
|
||||
assert!(!endpoint_debug.contains("X-ORBIT-KEY"), "OrbitFlare Customer API credentials must not enter Yellowstone settings");
|
||||
let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::connect(&endpoint).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
panic!("OrbitFlare Devnet Yellowstone connection without metadata must succeed or classify the provider auth gate: {error:?}")
|
||||
},
|
||||
};
|
||||
assert_eq!(channel.endpoint_name(), "orbitflare_devnet_yellowstone");
|
||||
assert_eq!(channel.provider().as_str(), "orbitflare");
|
||||
assert_eq!(channel.cluster().as_str(), "devnet");
|
||||
let request = match slot_request() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("OrbitFlare Yellowstone slot request must be valid: {error:?}"),
|
||||
};
|
||||
let mut session = match channel.open_standard_subscribe(request).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
panic!("OrbitFlare Devnet standard Subscribe without metadata must open or classify the provider auth gate: {error:?}")
|
||||
},
|
||||
};
|
||||
let deadline = tokio::time::sleep(std::time::Duration::from_secs(45));
|
||||
tokio::pin!(deadline);
|
||||
let mut slot_seen = false;
|
||||
let mut ping_seen = false;
|
||||
loop {
|
||||
tokio::select! {
|
||||
() = &mut deadline => break,
|
||||
next_update = session.next_update() => {
|
||||
let next_update = match next_update {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("OrbitFlare Devnet Yellowstone Subscribe update must decode: {error:?}"),
|
||||
};
|
||||
match next_update {
|
||||
std::option::Option::Some(ksp_onchain_transport_lib::YellowstoneSubscribeUpdate::Slot(update)) => {
|
||||
if update.slot() > 0 {
|
||||
slot_seen = true;
|
||||
}
|
||||
},
|
||||
std::option::Option::Some(ksp_onchain_transport_lib::YellowstoneSubscribeUpdate::Ping(_)) => ping_seen = true,
|
||||
std::option::Option::Some(_) => {},
|
||||
std::option::Option::None => break,
|
||||
}
|
||||
if slot_seen && ping_seen {
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
let close_timeout_result = tokio::time::timeout(std::time::Duration::from_secs(7), session.close()).await;
|
||||
let close_result = match close_timeout_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
panic!("KSP Yellowstone Subscribe close must remain bounded beyond the configured five-second provider half-close deadline: {error}")
|
||||
},
|
||||
};
|
||||
match close_result {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => {
|
||||
assert_eq!(
|
||||
error.code(),
|
||||
ksp_onchain_transport_lib::ERROR_CODE_TIMEOUT,
|
||||
"after live characterization, OrbitFlare close may time out waiting for provider half-close but must not fail for another reason: {error:?}"
|
||||
);
|
||||
},
|
||||
}
|
||||
assert!(slot_seen, "OrbitFlare Devnet Yellowstone must publish at least one non-zero confirmed slot during the 45-second characterization window");
|
||||
assert!(
|
||||
ping_seen,
|
||||
"OrbitFlare Devnet must emit the standard Yellowstone server Ping during the 45-second characterization window before any provider-specific heartbeat is justified"
|
||||
);
|
||||
}
|
||||
287
deltas/0.2.10/pre.002.md
Normal file
287
deltas/0.2.10/pre.002.md
Normal file
@@ -0,0 +1,287 @@
|
||||
<!-- file: deltas/0.2.10/pre.002.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Delta `0.2.10-pre.002` — OrbitFlare Devnet Config + characterization smoke
|
||||
|
||||
## 1. Base et version
|
||||
|
||||
Base de travail :
|
||||
|
||||
```text
|
||||
0.2.10-pre.001
|
||||
```
|
||||
|
||||
Validation opérateur fournie sur cette base le 2026-08-25 :
|
||||
|
||||
```text
|
||||
cargo fmt --all PASS
|
||||
python3 scripts/audit_rust_workspace_rules.py PASS
|
||||
python3 scripts/audit_markdown_tables.py PASS / 97 tables / 233 files
|
||||
cargo check --workspace PASS
|
||||
cargo clippy --workspace --all-targets PASS
|
||||
cargo test --workspace PASS
|
||||
cargo tree -p ksp-onchain-transport-lib fourni
|
||||
cargo tree -p ksp-onchain-transport-lib --duplicates fourni
|
||||
```
|
||||
|
||||
Le graphe confirme notamment :
|
||||
|
||||
```text
|
||||
yellowstone-grpc-proto 12.6.0
|
||||
tonic 0.14.6
|
||||
tonic-prost 0.14.6
|
||||
prost 0.14.4
|
||||
tokio 1.53.1
|
||||
```
|
||||
|
||||
Cette livraison passe à :
|
||||
|
||||
```text
|
||||
workspace.package.version = 0.2.10-pre.2
|
||||
commit attendu = v0.2.10-pre.002
|
||||
aucun tag prerelease
|
||||
```
|
||||
|
||||
## 2. Invariant architectural
|
||||
|
||||
Aucun moteur physique n'est modifié :
|
||||
|
||||
```text
|
||||
src/grpc_settings.rs inchangé
|
||||
src/grpc_channel.rs inchangé
|
||||
src/grpc_unary.rs inchangé
|
||||
src/grpc_subscribe.rs inchangé
|
||||
src/grpc_stream.rs inchangé
|
||||
```
|
||||
|
||||
Le standard Yellowstone N2 reste également inchangé.
|
||||
|
||||
OrbitFlare est matérialisé uniquement par :
|
||||
|
||||
```text
|
||||
Config provider profile
|
||||
provider-neutral Yellowstone smoke
|
||||
provider validation/documentation
|
||||
```
|
||||
|
||||
Aucun SDK OrbitFlare, second client Tonic, proto provider, heartbeat moteur ou metadata secrète n'est ajouté.
|
||||
|
||||
## 3. Profil Config V3 `orbitflare_devnet`
|
||||
|
||||
`config/std.transport.json` ajoute un profil dédié :
|
||||
|
||||
```text
|
||||
profile_id = orbitflare_devnet
|
||||
```
|
||||
|
||||
Il conserve les companions standards déjà utilisés sur Devnet :
|
||||
|
||||
```text
|
||||
HTTP = https://api.devnet.solana.com
|
||||
WS = wss://api.devnet.solana.com
|
||||
```
|
||||
|
||||
et ajoute exactement un endpoint Yellowstone :
|
||||
|
||||
```text
|
||||
name = orbitflare_solana_devnet_yellowstone
|
||||
provider = orbitflare
|
||||
cluster = devnet
|
||||
protocol = solana_yellowstone
|
||||
url = http://devnet.rpc.orbitflare.com:10000
|
||||
metadata = []
|
||||
secret_metadata = []
|
||||
```
|
||||
|
||||
Le format reste V3. `.env.example` n'est pas modifié et aucune API key de compte OrbitFlare n'est reclassifiée comme credential gRPC.
|
||||
|
||||
`ksp-config-lib` ajoute un test déterministe qui prouve :
|
||||
|
||||
```text
|
||||
sélection explicite orbitflare_devnet
|
||||
companions HTTP/WS Devnet préservés
|
||||
provider/cluster/protocol gRPC corrects
|
||||
URL plaintext conservée telle quelle
|
||||
aucune metadata inventée
|
||||
Debug gRPC sans URL provider
|
||||
absence de X-ORBIT-KEY dans Transport settings
|
||||
```
|
||||
|
||||
## 4. Smoke OrbitFlare Devnet
|
||||
|
||||
Nouveau test opt-in :
|
||||
|
||||
```text
|
||||
crates/ksp-onchain-transport-lib/tests/yellowstone_orbitflare_smoke.rs
|
||||
```
|
||||
|
||||
Le test utilise directement les API publiques Yellowstone existantes de Transport, sans dépendre de Config :
|
||||
|
||||
```text
|
||||
endpoint Devnet OrbitFlare sans metadata
|
||||
reconnect max_retries = 0
|
||||
Subscribe slots
|
||||
commitment = confirmed
|
||||
fenêtre de caractérisation = 45 s
|
||||
preuve Slot non nul
|
||||
preuve SubscribeUpdate::Ping serveur
|
||||
close borné
|
||||
```
|
||||
|
||||
Le `Ping` reçu reste traité par le moteur N1 déjà livré en `0.2.9`, qui répond automatiquement sur le même stream. Le test n'ajoute aucune policy heartbeat.
|
||||
|
||||
Commande live :
|
||||
|
||||
```bash
|
||||
cargo test -p ksp-onchain-transport-lib --test yellowstone_orbitflare_smoke -- --ignored --nocapture
|
||||
```
|
||||
|
||||
Interprétation :
|
||||
|
||||
```text
|
||||
Slot + Ping PASS
|
||||
OrbitFlare reste standard ; aucune façade/policy provider heartbeat requise
|
||||
|
||||
connection/auth failure
|
||||
classifier le service/licence réel avant toute metadata
|
||||
|
||||
Slot PASS mais Ping absent
|
||||
qualifier une divergence heartbeat provider avant toute implémentation spécifique
|
||||
```
|
||||
|
||||
## 5. Audit Markdown étendu
|
||||
|
||||
`scripts/audit_markdown_tables.py` passe en version 2 et contrôle désormais aussi l'équivalent structurel de :
|
||||
|
||||
```text
|
||||
\n\s*\n\s*\n
|
||||
```
|
||||
|
||||
c'est-à-dire au moins deux lignes vides consécutives hors fenced code block.
|
||||
|
||||
Le contrôle :
|
||||
|
||||
```text
|
||||
considère une ligne whitespace-only comme vide
|
||||
signale fichier + première ligne + longueur de la séquence
|
||||
n'interprète pas les blancs internes aux ``` / ~~~ fenced blocks
|
||||
ignore les répertoires générés ou tiers en parcours récursif
|
||||
```
|
||||
|
||||
Répertoires récursifs exclus :
|
||||
|
||||
```text
|
||||
.git
|
||||
.idea
|
||||
.venv
|
||||
__pycache__
|
||||
dist
|
||||
node_modules
|
||||
target
|
||||
```
|
||||
|
||||
Cette exclusion rend le résultat déterministe entre une archive Gitea propre et un checkout développeur contenant des `node_modules`. Le nombre de fichiers audités peut donc diminuer par rapport à l'ancien script sans signifier une perte de couverture KSP-owned.
|
||||
|
||||
Une seule séquence de deux lignes vides existait dans le scope KSP actif de l'archive : elle est supprimée dans `crates/ksp-onchain-transport-lib/README.md`.
|
||||
|
||||
`docs/rules/RULES_DOCUMENTATION.md` formalise `DOC-BLANK-001` à `DOC-BLANK-003`, y compris l'immuabilité des anciens deltas publiés.
|
||||
|
||||
## 6. Documents de plan/validation
|
||||
|
||||
Le plan 017 et la validation 013 passent en version 2 pour enregistrer :
|
||||
|
||||
```text
|
||||
pre.001 operator gate fermé
|
||||
profil orbitflare_devnet matérialisé
|
||||
smoke de caractérisation matérialisé
|
||||
N1/N2 inchangés
|
||||
live Devnet encore PENDING
|
||||
```
|
||||
|
||||
Aucun README/USAGE OrbitFlare durable n'est finalisé ici ; cette responsabilité reste réservée au couloir documentaire final.
|
||||
|
||||
## 7. Validation disponible dans l'environnement de génération
|
||||
|
||||
Exécuté sans Cargo :
|
||||
|
||||
```text
|
||||
python3 scripts/audit_rust_workspace_rules.py
|
||||
General Rust rule audit: clean
|
||||
Rust export completeness audit: 0 candidate(s)
|
||||
KSP workspace Rust rule audit: clean
|
||||
|
||||
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.10
|
||||
clean / 98 tables / 98 KSP-owned files
|
||||
|
||||
config/std.transport.json
|
||||
JSON parse PASS
|
||||
JSON Schema validation PASS
|
||||
```
|
||||
|
||||
Le nouvel audit Markdown a aussi été caractérisé avec des fixtures temporaires :
|
||||
|
||||
```text
|
||||
deux lignes vides hors fence FAIL attendu
|
||||
plusieurs lignes vides dans fence PASS attendu
|
||||
node_modules récursif ignoré attendu
|
||||
```
|
||||
|
||||
Aucune commande Cargo n'est déclarée exécutée dans l'environnement de génération.
|
||||
|
||||
## 8. Gates opérateur `pre.002`
|
||||
|
||||
Exécuter :
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
python3 scripts/audit_rust_workspace_rules.py
|
||||
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.10
|
||||
cargo check --workspace
|
||||
cargo clippy --workspace --all-targets
|
||||
cargo test -p ksp-config-lib
|
||||
cargo test -p ksp-onchain-transport-lib
|
||||
cargo test -p ksp-core-lib --test workspace_dependencies
|
||||
cargo test --workspace
|
||||
cargo tree -p ksp-onchain-transport-lib
|
||||
cargo tree -p ksp-onchain-transport-lib --duplicates
|
||||
```
|
||||
|
||||
Puis le live dédié :
|
||||
|
||||
```bash
|
||||
cargo test -p ksp-onchain-transport-lib --test yellowstone_orbitflare_smoke -- --ignored --nocapture
|
||||
```
|
||||
|
||||
`pre.002` ne ferme son gate provider que lorsque le résultat live est classifié. Un échec d'auth ou l'absence de `Ping` ne doit jamais être corrigé par modification du moteur N1.
|
||||
|
||||
## 9. Fichiers ajoutés/modifiés
|
||||
|
||||
```text
|
||||
Cargo.toml
|
||||
config/std.transport.json
|
||||
crates/ksp-config-lib/unit_tests/transport.rs
|
||||
crates/ksp-onchain-transport-lib/README.md
|
||||
crates/ksp-onchain-transport-lib/tests/yellowstone_orbitflare_smoke.rs
|
||||
docs/plans/017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md
|
||||
docs/rules/RULES_DOCUMENTATION.md
|
||||
docs/validation/013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md
|
||||
scripts/audit_markdown_tables.py
|
||||
deltas/0.2.10/pre.002.md
|
||||
```
|
||||
|
||||
## 10. Suite conditionnelle
|
||||
|
||||
Si le live retourne `Slot + Ping PASS` :
|
||||
|
||||
```text
|
||||
pre.003 = gate technique/live final
|
||||
```
|
||||
|
||||
Si une divergence provider réelle est démontrée :
|
||||
|
||||
```text
|
||||
pre.003 = tranche provider-specific minimale au-dessus de N1/N2
|
||||
pre.004 = gate technique/live final
|
||||
```
|
||||
|
||||
Le couloir documentaire final puis la prerelease de publication restent distincts dans les deux cas.
|
||||
@@ -1,9 +1,9 @@
|
||||
<!-- file: docs/plans/017-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC_PLAN.md -->
|
||||
<!-- version: 1 -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Plan `0.2.10` — OrbitFlare Yellowstone gRPC
|
||||
|
||||
**Statut courant : `0.2.10-pre.001` ouvre la release par un audit provider actuel. Le gate d'architecture est positif avec une contrainte renforcée : le moteur gRPC N1 et le standard Yellowstone N2 livrés par `0.2.9` sont immuables pour cette release. OrbitFlare doit être composé au-dessus de ces couches ; aucune modification du moteur n'est autorisée pour satisfaire une policy provider.**
|
||||
**Statut courant : `0.2.10-pre.002` matérialise le profil Config V3 `orbitflare_devnet` et un smoke opt-in de caractérisation `Subscribe -> Slot + Ping` construit exclusivement sur le standard Yellowstone existant. Le moteur gRPC N1 et le standard N2 restent inchangés ; le verdict live Devnet est un gate opérateur de cette tranche.**
|
||||
|
||||
## 1. Base et autorité
|
||||
|
||||
@@ -158,21 +158,21 @@ Une façade OrbitFlare publique n'est créée que si un consumer KSP doit réell
|
||||
|
||||
Preuves fournies le 2026-08-25 :
|
||||
|
||||
| Gate | Résultat |
|
||||
|-------------------------------------------|----------------------------------|
|
||||
| `cargo fmt --all` | PASS |
|
||||
| audit Rust workspace | PASS, 0 candidate export |
|
||||
| audit Markdown tables | PASS, 87 tables et 261 files |
|
||||
| `cargo check --workspace` | PASS |
|
||||
| `cargo clippy --workspace --all-targets` | PASS |
|
||||
| `cargo test --workspace` | PASS |
|
||||
| Transport unit | 383 sur 383 PASS |
|
||||
| Transport public API | 49 sur 49 PASS |
|
||||
| Transport release completeness | 43 sur 43 PASS |
|
||||
| Config unit | 113 sur 113 PASS |
|
||||
| PublicNode Yellowstone live | ignored opt-in comme attendu |
|
||||
| `cargo tree --duplicates` Transport | fourni et inspecté |
|
||||
| `cargo tree -p ksp-onchain-transport-lib` | à rejouer avant commit `pre.001` |
|
||||
| Gate | Résultat |
|
||||
|-------------------------------------------|------------------------------------------|
|
||||
| `cargo fmt --all` | PASS |
|
||||
| audit Rust workspace | PASS, 0 candidate export |
|
||||
| audit Markdown tables | PASS, 97 tables et 233 files sur `pre.1` |
|
||||
| `cargo check --workspace` | PASS |
|
||||
| `cargo clippy --workspace --all-targets` | PASS |
|
||||
| `cargo test --workspace` | PASS |
|
||||
| Transport unit | 383 sur 383 PASS |
|
||||
| Transport public API | 49 sur 49 PASS |
|
||||
| Transport release completeness | 43 sur 43 PASS |
|
||||
| Config unit | 113 sur 113 PASS |
|
||||
| PublicNode Yellowstone live | ignored opt-in comme attendu |
|
||||
| `cargo tree --duplicates` Transport | fourni et inspecté |
|
||||
| `cargo tree -p ksp-onchain-transport-lib` | fourni sur `0.2.10-pre.1` |
|
||||
|
||||
Versions visibles dans le graphe fourni :
|
||||
|
||||
@@ -419,12 +419,24 @@ pas de heartbeat dans grpc_defaults
|
||||
pas de secret tant que le Devnet Free n'en exige pas
|
||||
```
|
||||
|
||||
Profil prévu :
|
||||
Profil matérialisé en `pre.002` :
|
||||
|
||||
```text
|
||||
orbitflare_devnet
|
||||
```
|
||||
|
||||
Le profil conserve les companions HTTP/WS Solana Devnet standards et ajoute exactement un endpoint gRPC :
|
||||
|
||||
```text
|
||||
name = orbitflare_solana_devnet_yellowstone
|
||||
provider = orbitflare
|
||||
cluster = devnet
|
||||
protocol = solana_yellowstone
|
||||
url = http://devnet.rpc.orbitflare.com:10000
|
||||
metadata = []
|
||||
secret = []
|
||||
```
|
||||
|
||||
Si le service opérateur réel impose ensuite un token gRPC :
|
||||
|
||||
```text
|
||||
@@ -437,7 +449,7 @@ Cette variable n'est ajoutée à `.env.example` qu'après preuve qu'un secret da
|
||||
|
||||
### 12.1 Premier canari
|
||||
|
||||
Le premier canari doit utiliser **le standard N2 existant directement** :
|
||||
`pre.002` ajoute `tests/yellowstone_orbitflare_smoke.rs`. Ce canari opt-in utilise **le standard N2 existant directement** et ne dépend pas de Config, conformément à la frontière Transport -> Config interdite :
|
||||
|
||||
```text
|
||||
endpoint = http://devnet.rpc.orbitflare.com:10000
|
||||
@@ -533,8 +545,8 @@ pre.001 audit actuel + architecture immuable N1/N2 + auth/endpoints + free Devn
|
||||
preuve : plan 017 + validation 013 + delta + baseline
|
||||
|
||||
pre.002 profil Config V3 OrbitFlare Devnet + smoke de caractérisation standard N2
|
||||
preuve : Subscribe -> Slot + observation Ping serveur si disponible + probes provider ciblés
|
||||
aucune modification moteur gRPC
|
||||
statique : profil sans metadata + canari Subscribe slots confirmed + fenêtre Ping 45 s + close borné
|
||||
live : Subscribe -> Slot + observation Ping serveur ; aucune modification moteur gRPC
|
||||
|
||||
pre.003 soit gate technique final si OrbitFlare reste 100 pourcent standard,
|
||||
soit tranche provider-specific minimale uniquement si pre.002 démontre une divergence réelle
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/rules/RULES_DOCUMENTATION.md -->
|
||||
<!-- version: 5 -->
|
||||
<!-- version: 6 -->
|
||||
|
||||
# Règles de documentation
|
||||
|
||||
@@ -41,6 +41,12 @@ Les règles `DOC-*` s'appliquent aux documents Markdown internes et à leur orga
|
||||
- **DOC-TABLE-005** — Lorsqu’une modification touche une ligne d’un tableau, le tableau entier est réaligné avant livraison. `python3 scripts/audit_markdown_tables.py <fichiers-markdown-modifiés>` est le canari mécanique recommandé pour les fichiers concernés ; les blocs de code fenced ne sont pas interprétés comme des tableaux.
|
||||
- **DOC-TABLE-006** — L’immuabilité des deltas déjà publiés prime sur un reformatage rétrospectif : un ancien fichier `deltas/` n’est jamais réécrit uniquement pour satisfaire une règle de présentation introduite ultérieurement. Tout nouveau delta et tout autre tableau modifiable créé ou touché doivent en revanche respecter immédiatement `DOC-TABLE-001` à `DOC-TABLE-005`.
|
||||
|
||||
## Espacement vertical Markdown
|
||||
|
||||
- **DOC-BLANK-001** — Hors bloc de code fenced, un document Markdown KSP ne contient jamais plus d’une ligne vide consécutive. Une ligne composée uniquement d’espaces ou de tabulations est considérée comme vide.
|
||||
- **DOC-BLANK-002** — `python3 scripts/audit_markdown_tables.py <fichiers-ou-répertoires>` contrôle aussi les lignes vides multiples. Lors d’un parcours récursif, les répertoires générés ou tiers tels que `node_modules`, `dist`, `target`, `.git`, `.idea`, `.venv` et `__pycache__` sont exclus afin que le résultat porte uniquement sur les Markdown KSP-owned. Les blocs de code fenced conservent librement leur espacement interne.
|
||||
- **DOC-BLANK-003** — Les deltas déjà publiés restent immuables et ne sont jamais réécrits uniquement pour satisfaire `DOC-BLANK-001`. Les audits de release portent sur le répertoire de delta actif et sur les autres Markdown modifiables ; un audit historique explicite peut donc signaler des écarts hérités sans autoriser leur correction rétroactive.
|
||||
|
||||
## Contenu et exactitude
|
||||
|
||||
- **DOC-CONTENT-001** — Une documentation décrit l'état réellement décidé ou validé et distingue explicitement les hypothèses, propositions, TODO et questions ouvertes.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<!-- file: docs/validation/013-V0_2_10_ORBITFLARE_YELLOWSTONE_GRPC.md -->
|
||||
<!-- version: 1 -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Validation `0.2.10` — OrbitFlare Yellowstone gRPC
|
||||
|
||||
**Statut courant : matrice ouverte par `0.2.10-pre.001`. Le gate d'architecture est positif. Le premier objectif live est le Devnet gratuit OrbitFlare via le standard Yellowstone KSP inchangé. Les verdicts provider non encore testés restent explicitement `PENDING` ou `UNKNOWN`.**
|
||||
**Statut courant : `0.2.10-pre.002` matérialise le profil Config V3 OrbitFlare Devnet et le smoke opt-in de caractérisation sur le standard Yellowstone KSP inchangé. Les preuves statiques sont fermées ; `connect`, `Subscribe`, `Slot` et `Ping` restent `PENDING` jusqu'au run live opérateur.**
|
||||
|
||||
## 1. Autorités
|
||||
|
||||
@@ -36,7 +36,7 @@ Yellowstone upstream actuel
|
||||
| Transport release completeness | 43 sur 43 PASS |
|
||||
| Config unit | 113 sur 113 PASS |
|
||||
| graph duplicates Transport | fourni, aucun conflit N1 visible |
|
||||
| graph complet Transport | PENDING avant commit |
|
||||
| graph complet Transport | fourni sur `0.2.10-pre.1` PASS |
|
||||
|
||||
## 3. Invariant architecture
|
||||
|
||||
@@ -99,7 +99,7 @@ KSP ne prétend pas qu'un canal gRPC plaintext bénéficie d'une sécurité TLS
|
||||
| aucune metadata gRPC | SDK Go OrbitFlare | **DOCUMENTED** |
|
||||
| auth Devnet Free réelle | smoke KSP à exécuter | **PENDING** |
|
||||
|
||||
Aucun secret opérateur n'est demandé pour `pre.001`.
|
||||
Aucun secret opérateur n’est demandé pour le premier smoke `pre.002` ; la tentative initiale est explicitement sans metadata.
|
||||
|
||||
## 7. Heartbeat
|
||||
|
||||
@@ -163,17 +163,17 @@ Ces valeurs restent provider-owned et ne deviennent pas des validations généri
|
||||
|
||||
## 11. Config V3
|
||||
|
||||
| Exigence | Verdict |
|
||||
|-------------------------------------|-------------|
|
||||
| nouveau format Config nécessaire | **NO** |
|
||||
| `provider = orbitflare` | **PLANNED** |
|
||||
| `protocol = solana_yellowstone` | **PASS** |
|
||||
| `cluster = devnet` | **PLANNED** |
|
||||
| Devnet URL publique | **PLANNED** |
|
||||
| secret metadata par défaut | **NO** |
|
||||
| `X-ORBIT-KEY` dans Transport Config | **FORBID** |
|
||||
| `x-token` si token gRPC prouvé | **DEFER** |
|
||||
| heartbeat dans `grpc_defaults` | **NO** |
|
||||
| Exigence | Verdict |
|
||||
|-------------------------------------|-----------------|
|
||||
| nouveau format Config nécessaire | **NO** |
|
||||
| `provider = orbitflare` | **PASS STATIC** |
|
||||
| `protocol = solana_yellowstone` | **PASS STATIC** |
|
||||
| `cluster = devnet` | **PASS STATIC** |
|
||||
| Devnet URL publique | **PASS STATIC** |
|
||||
| secret metadata par défaut | **NO / PASS** |
|
||||
| `X-ORBIT-KEY` dans Transport Config | **FORBID** |
|
||||
| `x-token` si token gRPC prouvé | **DEFER** |
|
||||
| heartbeat dans `grpc_defaults` | **NO** |
|
||||
|
||||
## 12. Smoke Devnet attendu
|
||||
|
||||
@@ -194,15 +194,15 @@ close borné
|
||||
|
||||
Verdicts actuels :
|
||||
|
||||
| Preuve | Verdict |
|
||||
|-----------------------------|-------------|
|
||||
| endpoint parsé par N1 | EXPECTED |
|
||||
| connect Devnet | **PENDING** |
|
||||
| Subscribe accepté | **PENDING** |
|
||||
| Slot reçu | **PENDING** |
|
||||
| server Ping reçu | **PENDING** |
|
||||
| auto reply KSP déterministe | **PASS** |
|
||||
| close borné | **PASS N1** |
|
||||
| Preuve | Verdict |
|
||||
|------------------------------|----------------------------|
|
||||
| endpoint parsé par Config/N1 | **PASS STATIC** |
|
||||
| connect Devnet | **PENDING** |
|
||||
| Subscribe accepté | **PENDING** |
|
||||
| Slot reçu | **PENDING** |
|
||||
| server Ping reçu | **PENDING** |
|
||||
| auto reply KSP déterministe | **PASS N1** |
|
||||
| close borné | **PASS N1 / LIVE PENDING** |
|
||||
|
||||
## 13. Provider overlay gate
|
||||
|
||||
@@ -246,7 +246,7 @@ ownership/close provider bornés
|
||||
| baseline fmt/audits/check/clippy | **PASS** |
|
||||
| workspace test | **PASS** |
|
||||
| graph duplicates | **PASS** |
|
||||
| graph complet Transport | **PENDING OPERATOR** |
|
||||
| graph complet Transport | **PASS** |
|
||||
| sources OrbitFlare actuelles | **PASS** |
|
||||
| sources Yellowstone actuelles | **PASS** |
|
||||
| Free Devnet gRPC confirmé | **PASS** |
|
||||
@@ -267,6 +267,47 @@ Verdict :
|
||||
|
||||
```text
|
||||
architecture/sizing gate = POSITIVE
|
||||
operator commit gate = PENDING full cargo tree + validations overlay
|
||||
live provider gate = reserved for pre.002+
|
||||
operator pre.001 gate = PASS
|
||||
pre.002 static gate = READY FOR OPERATOR VALIDATION
|
||||
live provider gate = PENDING pre.002 characterization
|
||||
```
|
||||
|
||||
## 16. Gate `pre.002` candidat
|
||||
|
||||
| Critère | Verdict |
|
||||
|-------------------------------------------|----------------------|
|
||||
| Cargo `0.2.10-pre.2` | **PASS STATIC** |
|
||||
| profil Config `orbitflare_devnet` | **PASS STATIC** |
|
||||
| endpoint gRPC sans metadata | **PASS STATIC** |
|
||||
| companions HTTP/WS Devnet préservés | **PASS STATIC** |
|
||||
| nouveau format Config | **NO** |
|
||||
| `.env.example` / secret OrbitFlare ajouté | **NO** |
|
||||
| moteur N1 modifié | **NO** |
|
||||
| standard N2 modifié | **NO** |
|
||||
| SDK OrbitFlare ajouté | **NO** |
|
||||
| smoke provider dans Transport | **PASS STATIC** |
|
||||
| reconnect du smoke | zéro retry |
|
||||
| fenêtre caractérisation | 45 secondes |
|
||||
| `Subscribe -> Slot` live | **PENDING OPERATOR** |
|
||||
| `SubscribeUpdate::Ping` live | **PENDING OPERATOR** |
|
||||
| close live | **PENDING OPERATOR** |
|
||||
| auth Devnet Free sans metadata | **PENDING OPERATOR** |
|
||||
|
||||
Commande live dédiée :
|
||||
|
||||
```bash
|
||||
cargo test -p ksp-onchain-transport-lib --test yellowstone_orbitflare_smoke -- --ignored --nocapture
|
||||
```
|
||||
|
||||
Le résultat live décide la suite :
|
||||
|
||||
```text
|
||||
Slot + Ping PASS
|
||||
-> OrbitFlare reste standard ; aucun provider heartbeat/facade
|
||||
|
||||
connection/auth failure
|
||||
-> classifier le service/licence réel avant toute metadata
|
||||
|
||||
Slot PASS mais Ping absent
|
||||
-> divergence heartbeat à prouver avant une couche provider-specific
|
||||
```
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
# file: scripts/audit_markdown_tables.py
|
||||
# version: 1
|
||||
# version: 2
|
||||
|
||||
"""Validate KSP Markdown table formatting for explicitly supplied files or directories."""
|
||||
"""Validate KSP Markdown tables and vertical spacing for explicitly supplied files or directories."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -12,6 +12,7 @@ import re
|
||||
import sys
|
||||
|
||||
_SEPARATOR_CELL = re.compile(r"^:?-{3,}:?$")
|
||||
_IGNORED_DIRECTORY_NAMES = frozenset({".git", ".idea", ".venv", "__pycache__", "dist", "node_modules", "target"})
|
||||
|
||||
|
||||
def _markdown_files(paths: list[str]) -> list[pathlib.Path]:
|
||||
@@ -19,7 +20,13 @@ def _markdown_files(paths: list[str]) -> list[pathlib.Path]:
|
||||
for raw_path in paths:
|
||||
path = pathlib.Path(raw_path)
|
||||
if path.is_dir():
|
||||
files.extend(sorted(candidate for candidate in path.rglob("*.md") if candidate.is_file()))
|
||||
files.extend(
|
||||
sorted(
|
||||
candidate
|
||||
for candidate in path.rglob("*.md")
|
||||
if candidate.is_file() and not any(part in _IGNORED_DIRECTORY_NAMES for part in candidate.parts)
|
||||
)
|
||||
)
|
||||
elif path.is_file() and path.suffix.lower() == ".md":
|
||||
files.append(path)
|
||||
else:
|
||||
@@ -101,9 +108,47 @@ def _validate_table(path: pathlib.Path, start_line: int, rows: list[str]) -> lis
|
||||
return errors
|
||||
|
||||
|
||||
def _validate_blank_lines(path: pathlib.Path, lines: list[str]) -> list[str]:
|
||||
errors: list[str] = []
|
||||
fence_marker: str | None = None
|
||||
blank_run_start: int | None = None
|
||||
blank_run_length = 0
|
||||
|
||||
def flush_blank_run() -> None:
|
||||
nonlocal blank_run_start, blank_run_length
|
||||
if blank_run_start is not None and blank_run_length >= 2:
|
||||
errors.append(
|
||||
f"{path}:{blank_run_start}: {blank_run_length} consecutive blank lines are forbidden outside fenced code blocks; keep at most one"
|
||||
)
|
||||
blank_run_start = None
|
||||
blank_run_length = 0
|
||||
|
||||
for line_number, line in enumerate(lines, start=1):
|
||||
stripped = line.lstrip()
|
||||
if stripped.startswith("```") or stripped.startswith("~~~"):
|
||||
flush_blank_run()
|
||||
marker = stripped[:3]
|
||||
if fence_marker is None:
|
||||
fence_marker = marker
|
||||
elif marker == fence_marker:
|
||||
fence_marker = None
|
||||
continue
|
||||
if fence_marker is not None:
|
||||
continue
|
||||
if line.strip() == "":
|
||||
if blank_run_start is None:
|
||||
blank_run_start = line_number
|
||||
blank_run_length += 1
|
||||
continue
|
||||
flush_blank_run()
|
||||
|
||||
flush_blank_run()
|
||||
return errors
|
||||
|
||||
|
||||
def _audit_file(path: pathlib.Path) -> tuple[int, list[str]]:
|
||||
lines = path.read_text(encoding="utf-8").splitlines()
|
||||
errors: list[str] = []
|
||||
errors = _validate_blank_lines(path, lines)
|
||||
table_count = 0
|
||||
fence_marker: str | None = None
|
||||
index = 0
|
||||
@@ -135,7 +180,7 @@ def _audit_file(path: pathlib.Path) -> tuple[int, list[str]]:
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Audit Markdown tables in the explicitly selected scope."""
|
||||
"""Audit KSP Markdown formatting in the explicitly selected scope."""
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("paths", nargs="+", help="Markdown files or directories to audit")
|
||||
|
||||
Reference in New Issue
Block a user