10 Commits

Author SHA1 Message Date
033912fb2c v0.1.1-rel.001 2026-08-14 16:09:28 +02:00
11ca53ba48 v0.1.1-pre.005 2026-08-14 16:04:45 +02:00
b01fb3fa53 v0.1.1-pre.004 2026-08-14 15:54:52 +02:00
4a86a76ca8 v0.1.1-pre.003-fix.001 2026-08-14 15:40:48 +02:00
75e5ec047f v0.1.1-pre.003 2026-08-14 15:35:19 +02:00
81eda2e5ad v0.1.1-pre.002-fix.001 2026-08-14 15:18:23 +02:00
72ddabd9f2 v0.1.1-pre.002 2026-08-14 15:12:38 +02:00
27a6715a3e v0.1.1-pre.001-fix.002 2026-08-14 14:45:57 +02:00
37a1480c72 v0.1.1-pre.001-fix.001 2026-08-14 14:26:43 +02:00
ecc82f681d v0.1.1-pre.001 2026-08-14 14:11:27 +02:00
26 changed files with 4053 additions and 43 deletions

View File

@@ -1,18 +1,21 @@
# file: Cargo.toml # file: Cargo.toml
# version: 17 # version: 25
[workspace] [workspace]
resolver = "3" resolver = "3"
members = ["crates/ksp-core-lib"] members = ["crates/ksp-core-lib"]
[workspace.package] [workspace.package]
version = "0.0.3" version = "0.1.1"
edition = "2024" edition = "2024"
license = "MIT" license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
authors = ["SinuS von SifriduS <sinus@sasedev.net>"] authors = ["SinuS von SifriduS <sinus@sasedev.net>"]
publish = false publish = false
[workspace.dependencies]
solana-pubkey = { version = "^4.3", default-features = false }
[workspace.lints.rust] [workspace.lints.rust]
missing_docs = "warn" missing_docs = "warn"
unreachable_pub = "deny" unreachable_pub = "deny"

View File

@@ -1,5 +1,5 @@
<!-- file: ROADMAP.md --> <!-- file: ROADMAP.md -->
<!-- version: 12 --> <!-- version: 13 -->
# Roadmap KSP # Roadmap KSP
@@ -31,7 +31,7 @@ Regrouper les releases consacrées aux fondations N1. Chaque release concrète e
### Releases concrètes ### Releases concrètes
- [ ] `0.1.1` — Stabiliser `ksp-core-lib` : `Error`/`Result`, Program IDs fondamentaux et primitives réellement N1. - [X] `0.1.1` — Stabiliser `ksp-core-lib` : `Error`/`Result`, Program IDs fondamentaux et primitives réellement N1.
- [ ] `0.1.2` — Introduire `ksp-logging-lib` comme façade KSP de `tracing`, `tracing-appender` et `tracing-subscriber`. - [ ] `0.1.2` — Introduire `ksp-logging-lib` comme façade KSP de `tracing`, `tracing-appender` et `tracing-subscriber`.
- [ ] `0.1.3` — Introduire `ksp-config-lib` : documents, profils, résolution, validation et modifications autorisées. - [ ] `0.1.3` — Introduire `ksp-config-lib` : documents, profils, résolution, validation et modifications autorisées.
- [ ] `0.1.4` — Introduire `ksp-app-config-desk` pour valider réellement Config et la frontière Tauri. - [ ] `0.1.4` — Introduire `ksp-app-config-desk` pour valider réellement Config et la frontière Tauri.

View File

@@ -1,5 +1,5 @@
# file: crates/ksp-core-lib/Cargo.toml # file: crates/ksp-core-lib/Cargo.toml
# version: 1 # version: 3
[package] [package]
name = "ksp-core-lib" name = "ksp-core-lib"
@@ -7,5 +7,8 @@ version.workspace = true
edition.workspace = true edition.workspace = true
repository.workspace = true repository.workspace = true
[dependencies]
solana-pubkey.workspace = true
[lints] [lints]
workspace = true workspace = true

View File

@@ -0,0 +1,130 @@
// file: crates/ksp-core-lib/src/error.rs
// version: 1
/// Stable structured identifier for a KSP error.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ErrorCode {
domain: &'static str,
code: &'static str,
}
impl ErrorCode {
/// Creates an error code from a stable domain and code identifier.
#[must_use]
pub const fn new(domain: &'static str, code: &'static str) -> Self {
return Self { domain, code };
}
/// Returns the stable error domain identifier.
#[must_use]
pub const fn domain(&self) -> &'static str {
return self.domain;
}
/// Returns the stable error code identifier within the domain.
#[must_use]
pub const fn code(&self) -> &'static str {
return self.code;
}
}
/// Structured contextual field attached to a KSP error.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ErrorContext {
key: &'static str,
value: std::string::String,
}
impl ErrorContext {
/// Creates one contextual field from a stable key and an owned value.
#[must_use]
pub fn new(key: &'static str, value: impl std::convert::Into<std::string::String>) -> Self {
return Self { key, value: value.into() };
}
/// Returns the stable contextual key.
#[must_use]
pub fn key(&self) -> &'static str {
return self.key;
}
/// Returns the contextual value.
#[must_use]
pub fn value(&self) -> &str {
return self.value.as_str();
}
}
/// Common KSP error carrying a stable code, human-readable message, structured context and optional source.
#[derive(Debug)]
pub struct Error {
code: crate::ErrorCode,
message: std::string::String,
context: std::vec::Vec<crate::ErrorContext>,
source: std::option::Option<std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync + 'static>>,
}
impl Error {
/// Creates a KSP error without context or external source.
#[must_use]
pub fn new(code: crate::ErrorCode, message: impl std::convert::Into<std::string::String>) -> Self {
return Self { code, message: message.into(), context: std::vec::Vec::new(), source: std::option::Option::None };
}
/// Returns the stable structured error code.
#[must_use]
pub const fn code(&self) -> crate::ErrorCode {
return self.code;
}
/// Returns the human-readable diagnostic message.
#[must_use]
pub fn message(&self) -> &str {
return self.message.as_str();
}
/// Returns the contextual fields in insertion order.
#[must_use]
pub fn context(&self) -> &[crate::ErrorContext] {
return self.context.as_slice();
}
/// Appends one contextual field and returns the enriched error.
#[must_use]
pub fn with_context(mut self, key: &'static str, value: impl std::convert::Into<std::string::String>) -> Self {
self.context.push(crate::ErrorContext::new(key, value));
return self;
}
/// Attaches an external error as the standard source and returns the enriched error.
#[must_use]
pub fn with_source<E>(mut self, source: E) -> Self
where
E: std::error::Error + std::marker::Send + std::marker::Sync + 'static,
{
self.source = std::option::Option::Some(std::boxed::Box::new(source));
return self;
}
}
impl std::fmt::Display for Error {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return write!(formatter, "{}.{}: {}", self.code.domain(), self.code.code(), self.message);
}
}
impl std::error::Error for Error {
fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
return match self.source.as_deref() {
std::option::Option::Some(source) => std::option::Option::Some(source),
std::option::Option::None => std::option::Option::None,
};
}
}
/// Common result type returned by KSP APIs using [`crate::Error`].
pub type Result<T> = std::result::Result<T, crate::Error>;
#[cfg(test)]
#[path = "../unit_tests/error.rs"]
mod tests;

View File

@@ -1,7 +1,120 @@
// file: crates/ksp-core-lib/src/lib.rs // file: crates/ksp-core-lib/src/lib.rs
// version: 3 // version: 6
#![warn(missing_docs)] #![warn(missing_docs)]
#![deny(unreachable_pub)] #![deny(unreachable_pub)]
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
//! Minimal core-library skeleton for the KSP foundation phase. //! Core contracts shared by the foundational KSP layers.
//!
//! `ksp-core-lib` owns the common KSP error contract, the Solana [`Pubkey`]
//! primitive used by the project, and the KSP-owned registry of fundamental
//! Solana Program IDs. Higher-level domains extend these contracts without
//! introducing reverse dependencies from Core.
mod error;
mod program_ids;
/// Common KSP error type used by higher-level crates.
pub use self::error::Error;
/// Stable structured code identifying a KSP error category and condition.
pub use self::error::ErrorCode;
/// Structured contextual field attached to a KSP error.
pub use self::error::ErrorContext;
/// Common KSP result alias using [`Error`].
pub use self::error::Result;
/// Canonical Address Lookup Table Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_ADDRESS_LOOKUP_TABLE;
/// Canonical Compute Budget Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_COMPUTE_BUDGET;
/// Canonical Config Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_CONFIG;
/// Canonical Feature Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_FEATURE;
/// Canonical upgradeable BPF Loader Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_LOADER_BPF_UPGRADEABLE;
/// Canonical deprecated BPF Loader Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_LOADER_BPF_V1;
/// Canonical BPF Loader v2 Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_LOADER_BPF_V2;
/// Canonical Native Loader Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_LOADER_NATIVE;
/// Canonical Loader v4 Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_LOADER_V4;
/// Canonical Ed25519 precompile Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_PRECOMPILE_ED25519;
/// Canonical Secp256k1 precompile Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_PRECOMPILE_SECP256K1;
/// Canonical Secp256r1 precompile Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_PRECOMPILE_SECP256R1;
/// Canonical Slashing Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_SLASHING;
/// Canonical Stake Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_STAKE;
/// Canonical System Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_SYSTEM;
/// Canonical Vote Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_VOTE;
/// Canonical ZK ElGamal Proof Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_ZK_ELGAMAL_PROOF;
/// Canonical ZK Token Proof Program ID as Base58 text.
pub use self::program_ids::PRGID_SOLANA_ZK_TOKEN_PROOF;
/// Canonical Address Lookup Table Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_ADDRESS_LOOKUP_TABLE;
/// Canonical Compute Budget Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_COMPUTE_BUDGET;
/// Canonical Config Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_CONFIG;
/// Canonical Feature Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_FEATURE;
/// Canonical upgradeable BPF Loader Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_LOADER_BPF_UPGRADEABLE;
/// Canonical deprecated BPF Loader Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_LOADER_BPF_V1;
/// Canonical BPF Loader v2 Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_LOADER_BPF_V2;
/// Canonical Native Loader Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_LOADER_NATIVE;
/// Canonical Loader v4 Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_LOADER_V4;
/// Canonical Ed25519 precompile Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_PRECOMPILE_ED25519;
/// Canonical Secp256k1 precompile Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_PRECOMPILE_SECP256K1;
/// Canonical Secp256r1 precompile Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_PRECOMPILE_SECP256R1;
/// Canonical Slashing Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_SLASHING;
/// Canonical Stake Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_STAKE;
/// Canonical System Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_SYSTEM;
/// Canonical Vote Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_VOTE;
/// Canonical ZK ElGamal Proof Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_ZK_ELGAMAL_PROOF;
/// Canonical ZK Token Proof Program ID as a typed [`Pubkey`].
pub use self::program_ids::PRGIDPK_SOLANA_ZK_TOKEN_PROOF;
/// Immutable descriptor for one registered Program ID.
pub use self::program_ids::ProgramIdEntry;
/// Borrowed multi-axis filter for the canonical Program ID registry.
pub use self::program_ids::ProgramIdFilter;
/// Technical classification of one registered Program ID.
pub use self::program_ids::ProgramIdKind;
/// Returns the canonical Program ID registry.
pub use self::program_ids::entries;
/// Finds one registered Program ID by its Base58 representation.
pub use self::program_ids::find_program_id;
/// Finds one registered Program ID by its typed [`Pubkey`] representation.
pub use self::program_ids::find_program_pubkey;
/// Returns the Solana core/native Program ID view.
pub use self::program_ids::native_program_ids;
/// Returns Program IDs matching a multi-axis filter.
pub use self::program_ids::program_ids;
/// Returns Program IDs belonging to one domain.
pub use self::program_ids::program_ids_by_domain;
/// Returns Program IDs belonging to one family.
pub use self::program_ids::program_ids_by_family;
/// Returns Program IDs belonging to one protocol or project.
pub use self::program_ids::program_ids_by_protocol;
/// Solana account address primitive used by KSP Program IDs.
pub use solana_pubkey::Pubkey;

View File

@@ -0,0 +1,492 @@
// file: crates/ksp-core-lib/src/program_ids.rs
// version: 2
const DOMAIN_SOLANA: &str = "solana";
const FAMILY_CONSENSUS: &str = "consensus";
const FAMILY_LOADER: &str = "loader";
const FAMILY_PRECOMPILE: &str = "precompile";
const FAMILY_PROOF: &str = "proof";
const FAMILY_RUNTIME: &str = "runtime";
const PROTOCOL_SOLANA: &str = "solana";
/// Declares one KSP-owned Solana Program ID as matching Base58 and typed constants.
///
/// The Base58 literal is written once and decoded at compile time through [`crate::Pubkey`].
#[macro_export]
macro_rules! declare_program_id {
($string_name:ident, $pubkey_name:ident, $value:literal) => {
#[doc = concat!("Base58 Program ID declared as `", stringify!($string_name), "`.")]
pub const $string_name: &str = $value;
#[doc = concat!("Typed Program ID corresponding to `", stringify!($string_name), "`.")]
pub const $pubkey_name: $crate::Pubkey = $crate::Pubkey::from_str_const($string_name);
};
}
crate::declare_program_id!(PRGID_SOLANA_ADDRESS_LOOKUP_TABLE, PRGIDPK_SOLANA_ADDRESS_LOOKUP_TABLE, "AddressLookupTab1e1111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_LOADER_BPF_V1, PRGIDPK_SOLANA_LOADER_BPF_V1, "BPFLoader1111111111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_LOADER_BPF_V2, PRGIDPK_SOLANA_LOADER_BPF_V2, "BPFLoader2111111111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_LOADER_BPF_UPGRADEABLE, PRGIDPK_SOLANA_LOADER_BPF_UPGRADEABLE, "BPFLoaderUpgradeab1e11111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_COMPUTE_BUDGET, PRGIDPK_SOLANA_COMPUTE_BUDGET, "ComputeBudget111111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_CONFIG, PRGIDPK_SOLANA_CONFIG, "Config1111111111111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_PRECOMPILE_ED25519, PRGIDPK_SOLANA_PRECOMPILE_ED25519, "Ed25519SigVerify111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_FEATURE, PRGIDPK_SOLANA_FEATURE, "Feature111111111111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_LOADER_V4, PRGIDPK_SOLANA_LOADER_V4, "LoaderV411111111111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_LOADER_NATIVE, PRGIDPK_SOLANA_LOADER_NATIVE, "NativeLoader1111111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_PRECOMPILE_SECP256K1, PRGIDPK_SOLANA_PRECOMPILE_SECP256K1, "KeccakSecp256k11111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_PRECOMPILE_SECP256R1, PRGIDPK_SOLANA_PRECOMPILE_SECP256R1, "Secp256r1SigVerify1111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_SLASHING, PRGIDPK_SOLANA_SLASHING, "S1ashing11111111111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_STAKE, PRGIDPK_SOLANA_STAKE, "Stake11111111111111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_SYSTEM, PRGIDPK_SOLANA_SYSTEM, "11111111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_VOTE, PRGIDPK_SOLANA_VOTE, "Vote111111111111111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_ZK_ELGAMAL_PROOF, PRGIDPK_SOLANA_ZK_ELGAMAL_PROOF, "ZkE1Gama1Proof11111111111111111111111111111");
crate::declare_program_id!(PRGID_SOLANA_ZK_TOKEN_PROOF, PRGIDPK_SOLANA_ZK_TOKEN_PROOF, "ZkTokenProof1111111111111111111111111111111");
/// Technical classification of a registered Program ID.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ProgramIdKind {
/// A regular executable program belonging to the registered protocol surface.
Program,
/// A Solana program loader.
Loader,
/// A runtime precompile exposed through a Program ID.
Precompile,
/// An enshrined on-chain program deployed as part of the Solana protocol.
EnshrinedProgram,
}
/// Immutable descriptor for one KSP-owned Program ID.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ProgramIdEntry {
code: &'static str,
name: &'static str,
program_id: &'static str,
pubkey: crate::Pubkey,
domain: &'static str,
family: &'static str,
protocol: &'static str,
subfamily: std::option::Option<&'static str>,
program_version: std::option::Option<&'static str>,
kind: crate::ProgramIdKind,
}
impl ProgramIdEntry {
const fn new(code: &'static str, name: &'static str, program_id: &'static str, pubkey: crate::Pubkey) -> Self {
return Self {
code,
name,
program_id,
pubkey,
domain: "",
family: "",
protocol: "",
subfamily: std::option::Option::None,
program_version: std::option::Option::None,
kind: crate::ProgramIdKind::Program,
};
}
const fn with_taxonomy(
mut self,
domain: &'static str,
family: &'static str,
protocol: &'static str,
subfamily: std::option::Option<&'static str>,
program_version: std::option::Option<&'static str>,
kind: crate::ProgramIdKind,
) -> Self {
self.domain = domain;
self.family = family;
self.protocol = protocol;
self.subfamily = subfamily;
self.program_version = program_version;
self.kind = kind;
return self;
}
/// Returns the stable KSP machine-readable code of this entry.
#[must_use]
pub const fn code(&self) -> &'static str {
return self.code;
}
/// Returns the human-readable program name.
#[must_use]
pub const fn name(&self) -> &'static str {
return self.name;
}
/// Returns the canonical Base58 Program ID.
#[must_use]
pub const fn program_id(&self) -> &'static str {
return self.program_id;
}
/// Returns the typed Solana Program ID.
#[must_use]
pub const fn pubkey(&self) -> crate::Pubkey {
return self.pubkey;
}
/// Returns the broad functional domain.
#[must_use]
pub const fn domain(&self) -> &'static str {
return self.domain;
}
/// Returns the functional family within the domain.
#[must_use]
pub const fn family(&self) -> &'static str {
return self.family;
}
/// Returns the owning protocol or project identifier.
#[must_use]
pub const fn protocol(&self) -> &'static str {
return self.protocol;
}
/// Returns the optional architectural branch or product subfamily.
#[must_use]
pub const fn subfamily(&self) -> std::option::Option<&'static str> {
return self.subfamily;
}
/// Returns the optional public generation of this program lineage.
#[must_use]
pub const fn program_version(&self) -> std::option::Option<&'static str> {
return self.program_version;
}
/// Returns the technical Program ID classification.
#[must_use]
pub const fn kind(&self) -> crate::ProgramIdKind {
return self.kind;
}
}
/// Borrowed filter used to select Program IDs from the canonical KSP registry.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct ProgramIdFilter<'a> {
domain: std::option::Option<&'a str>,
family: std::option::Option<&'a str>,
protocol: std::option::Option<&'a str>,
subfamily: std::option::Option<&'a str>,
program_version: std::option::Option<&'a str>,
kind: std::option::Option<crate::ProgramIdKind>,
}
impl<'a> ProgramIdFilter<'a> {
/// Creates an empty filter matching every registered Program ID.
#[must_use]
pub const fn new() -> Self {
return Self {
domain: std::option::Option::None,
family: std::option::Option::None,
protocol: std::option::Option::None,
subfamily: std::option::Option::None,
program_version: std::option::Option::None,
kind: std::option::Option::None,
};
}
/// Restricts the filter to one functional domain.
#[must_use]
pub const fn with_domain(mut self, domain: &'a str) -> Self {
self.domain = std::option::Option::Some(domain);
return self;
}
/// Restricts the filter to one functional family.
#[must_use]
pub const fn with_family(mut self, family: &'a str) -> Self {
self.family = std::option::Option::Some(family);
return self;
}
/// Restricts the filter to one protocol or project.
#[must_use]
pub const fn with_protocol(mut self, protocol: &'a str) -> Self {
self.protocol = std::option::Option::Some(protocol);
return self;
}
/// Restricts the filter to one architectural subfamily.
#[must_use]
pub const fn with_subfamily(mut self, subfamily: &'a str) -> Self {
self.subfamily = std::option::Option::Some(subfamily);
return self;
}
/// Restricts the filter to one public program generation.
#[must_use]
pub const fn with_program_version(mut self, program_version: &'a str) -> Self {
self.program_version = std::option::Option::Some(program_version);
return self;
}
/// Restricts the filter to one technical Program ID kind.
#[must_use]
pub const fn with_kind(mut self, kind: crate::ProgramIdKind) -> Self {
self.kind = std::option::Option::Some(kind);
return self;
}
fn matches(&self, entry: &crate::ProgramIdEntry) -> bool {
if let std::option::Option::Some(domain) = self.domain
&& entry.domain() != domain
{
return false;
}
if let std::option::Option::Some(family) = self.family
&& entry.family() != family
{
return false;
}
if let std::option::Option::Some(protocol) = self.protocol
&& entry.protocol() != protocol
{
return false;
}
if let std::option::Option::Some(subfamily) = self.subfamily
&& entry.subfamily() != std::option::Option::Some(subfamily)
{
return false;
}
if let std::option::Option::Some(program_version) = self.program_version
&& entry.program_version() != std::option::Option::Some(program_version)
{
return false;
}
if let std::option::Option::Some(kind) = self.kind
&& entry.kind() != kind
{
return false;
}
return true;
}
}
const PROGRAM_ID_ENTRIES: &[crate::ProgramIdEntry] = &[
crate::ProgramIdEntry::new(
"solana.address_lookup_table",
"Address Lookup Table Program",
crate::PRGID_SOLANA_ADDRESS_LOOKUP_TABLE,
crate::PRGIDPK_SOLANA_ADDRESS_LOOKUP_TABLE,
)
.with_taxonomy(DOMAIN_SOLANA, FAMILY_RUNTIME, PROTOCOL_SOLANA, std::option::Option::None, std::option::Option::None, crate::ProgramIdKind::Program),
crate::ProgramIdEntry::new("solana.loader.bpf.v1", "Deprecated BPF Loader", crate::PRGID_SOLANA_LOADER_BPF_V1, crate::PRGIDPK_SOLANA_LOADER_BPF_V1)
.with_taxonomy(
DOMAIN_SOLANA,
FAMILY_LOADER,
PROTOCOL_SOLANA,
std::option::Option::Some("bpf"),
std::option::Option::Some("v1"),
crate::ProgramIdKind::Loader,
),
crate::ProgramIdEntry::new("solana.loader.bpf.v2", "BPF Loader v2", crate::PRGID_SOLANA_LOADER_BPF_V2, crate::PRGIDPK_SOLANA_LOADER_BPF_V2).with_taxonomy(
DOMAIN_SOLANA,
FAMILY_LOADER,
PROTOCOL_SOLANA,
std::option::Option::Some("bpf"),
std::option::Option::Some("v2"),
crate::ProgramIdKind::Loader,
),
crate::ProgramIdEntry::new(
"solana.loader.bpf_upgradeable",
"Upgradeable BPF Loader",
crate::PRGID_SOLANA_LOADER_BPF_UPGRADEABLE,
crate::PRGIDPK_SOLANA_LOADER_BPF_UPGRADEABLE,
)
.with_taxonomy(
DOMAIN_SOLANA,
FAMILY_LOADER,
PROTOCOL_SOLANA,
std::option::Option::Some("bpf"),
std::option::Option::None,
crate::ProgramIdKind::Loader,
),
crate::ProgramIdEntry::new("solana.compute_budget", "Compute Budget Program", crate::PRGID_SOLANA_COMPUTE_BUDGET, crate::PRGIDPK_SOLANA_COMPUTE_BUDGET)
.with_taxonomy(DOMAIN_SOLANA, FAMILY_RUNTIME, PROTOCOL_SOLANA, std::option::Option::None, std::option::Option::None, crate::ProgramIdKind::Program),
crate::ProgramIdEntry::new("solana.config", "Config Program", crate::PRGID_SOLANA_CONFIG, crate::PRGIDPK_SOLANA_CONFIG).with_taxonomy(
DOMAIN_SOLANA,
FAMILY_RUNTIME,
PROTOCOL_SOLANA,
std::option::Option::None,
std::option::Option::None,
crate::ProgramIdKind::Program,
),
crate::ProgramIdEntry::new(
"solana.precompile.ed25519",
"Ed25519 Signature Verification Precompile",
crate::PRGID_SOLANA_PRECOMPILE_ED25519,
crate::PRGIDPK_SOLANA_PRECOMPILE_ED25519,
)
.with_taxonomy(
DOMAIN_SOLANA,
FAMILY_PRECOMPILE,
PROTOCOL_SOLANA,
std::option::Option::Some("ed25519"),
std::option::Option::None,
crate::ProgramIdKind::Precompile,
),
crate::ProgramIdEntry::new("solana.feature", "Feature Program", crate::PRGID_SOLANA_FEATURE, crate::PRGIDPK_SOLANA_FEATURE).with_taxonomy(
DOMAIN_SOLANA,
FAMILY_RUNTIME,
PROTOCOL_SOLANA,
std::option::Option::None,
std::option::Option::None,
crate::ProgramIdKind::Program,
),
crate::ProgramIdEntry::new("solana.loader.v4", "Loader v4", crate::PRGID_SOLANA_LOADER_V4, crate::PRGIDPK_SOLANA_LOADER_V4).with_taxonomy(
DOMAIN_SOLANA,
FAMILY_LOADER,
PROTOCOL_SOLANA,
std::option::Option::None,
std::option::Option::Some("v4"),
crate::ProgramIdKind::Loader,
),
crate::ProgramIdEntry::new("solana.loader.native", "Native Loader", crate::PRGID_SOLANA_LOADER_NATIVE, crate::PRGIDPK_SOLANA_LOADER_NATIVE).with_taxonomy(
DOMAIN_SOLANA,
FAMILY_LOADER,
PROTOCOL_SOLANA,
std::option::Option::Some("native"),
std::option::Option::None,
crate::ProgramIdKind::Loader,
),
crate::ProgramIdEntry::new(
"solana.precompile.secp256k1",
"Secp256k1 Signature Verification Precompile",
crate::PRGID_SOLANA_PRECOMPILE_SECP256K1,
crate::PRGIDPK_SOLANA_PRECOMPILE_SECP256K1,
)
.with_taxonomy(
DOMAIN_SOLANA,
FAMILY_PRECOMPILE,
PROTOCOL_SOLANA,
std::option::Option::Some("secp256k1"),
std::option::Option::None,
crate::ProgramIdKind::Precompile,
),
crate::ProgramIdEntry::new(
"solana.precompile.secp256r1",
"Secp256r1 Signature Verification Precompile",
crate::PRGID_SOLANA_PRECOMPILE_SECP256R1,
crate::PRGIDPK_SOLANA_PRECOMPILE_SECP256R1,
)
.with_taxonomy(
DOMAIN_SOLANA,
FAMILY_PRECOMPILE,
PROTOCOL_SOLANA,
std::option::Option::Some("secp256r1"),
std::option::Option::None,
crate::ProgramIdKind::Precompile,
),
crate::ProgramIdEntry::new("solana.slashing", "Slashing Program", crate::PRGID_SOLANA_SLASHING, crate::PRGIDPK_SOLANA_SLASHING).with_taxonomy(
DOMAIN_SOLANA,
FAMILY_CONSENSUS,
PROTOCOL_SOLANA,
std::option::Option::None,
std::option::Option::None,
crate::ProgramIdKind::EnshrinedProgram,
),
crate::ProgramIdEntry::new("solana.stake", "Stake Program", crate::PRGID_SOLANA_STAKE, crate::PRGIDPK_SOLANA_STAKE).with_taxonomy(
DOMAIN_SOLANA,
FAMILY_CONSENSUS,
PROTOCOL_SOLANA,
std::option::Option::None,
std::option::Option::None,
crate::ProgramIdKind::Program,
),
crate::ProgramIdEntry::new("solana.system", "System Program", crate::PRGID_SOLANA_SYSTEM, crate::PRGIDPK_SOLANA_SYSTEM).with_taxonomy(
DOMAIN_SOLANA,
FAMILY_RUNTIME,
PROTOCOL_SOLANA,
std::option::Option::None,
std::option::Option::None,
crate::ProgramIdKind::Program,
),
crate::ProgramIdEntry::new("solana.vote", "Vote Program", crate::PRGID_SOLANA_VOTE, crate::PRGIDPK_SOLANA_VOTE).with_taxonomy(
DOMAIN_SOLANA,
FAMILY_CONSENSUS,
PROTOCOL_SOLANA,
std::option::Option::None,
std::option::Option::None,
crate::ProgramIdKind::Program,
),
crate::ProgramIdEntry::new(
"solana.proof.zk_elgamal",
"ZK ElGamal Proof Program",
crate::PRGID_SOLANA_ZK_ELGAMAL_PROOF,
crate::PRGIDPK_SOLANA_ZK_ELGAMAL_PROOF,
)
.with_taxonomy(
DOMAIN_SOLANA,
FAMILY_PROOF,
PROTOCOL_SOLANA,
std::option::Option::Some("zk_elgamal"),
std::option::Option::None,
crate::ProgramIdKind::Program,
),
crate::ProgramIdEntry::new("solana.proof.zk_token", "ZK Token Proof Program", crate::PRGID_SOLANA_ZK_TOKEN_PROOF, crate::PRGIDPK_SOLANA_ZK_TOKEN_PROOF)
.with_taxonomy(
DOMAIN_SOLANA,
FAMILY_PROOF,
PROTOCOL_SOLANA,
std::option::Option::Some("zk_token"),
std::option::Option::None,
crate::ProgramIdKind::Program,
),
];
/// Returns the canonical KSP Program ID registry.
#[must_use]
pub const fn entries() -> &'static [crate::ProgramIdEntry] {
return PROGRAM_ID_ENTRIES;
}
/// Returns a lazy view of Program IDs matching all configured filter axes.
pub fn program_ids<'a>(filter: crate::ProgramIdFilter<'a>) -> impl std::iter::Iterator<Item = &'static crate::ProgramIdEntry> + 'a {
return PROGRAM_ID_ENTRIES.iter().filter(move |entry| {
return filter.matches(entry);
});
}
/// Returns all Solana core/native Program IDs, including loaders, precompiles and enshrined programs.
pub fn native_program_ids() -> impl std::iter::Iterator<Item = &'static crate::ProgramIdEntry> {
return crate::program_ids(crate::ProgramIdFilter::new().with_domain(DOMAIN_SOLANA).with_protocol(PROTOCOL_SOLANA));
}
/// Returns a lazy view of Program IDs belonging to one functional domain.
pub fn program_ids_by_domain<'a>(domain: &'a str) -> impl std::iter::Iterator<Item = &'static crate::ProgramIdEntry> + 'a {
return crate::program_ids(crate::ProgramIdFilter::new().with_domain(domain));
}
/// Returns a lazy view of Program IDs belonging to one functional family.
pub fn program_ids_by_family<'a>(family: &'a str) -> impl std::iter::Iterator<Item = &'static crate::ProgramIdEntry> + 'a {
return crate::program_ids(crate::ProgramIdFilter::new().with_family(family));
}
/// Returns a lazy view of Program IDs belonging to one protocol or project.
pub fn program_ids_by_protocol<'a>(protocol: &'a str) -> impl std::iter::Iterator<Item = &'static crate::ProgramIdEntry> + 'a {
return crate::program_ids(crate::ProgramIdFilter::new().with_protocol(protocol));
}
/// Finds one registered Program ID by its canonical Base58 representation.
#[must_use]
pub fn find_program_id(program_id: &str) -> std::option::Option<&'static crate::ProgramIdEntry> {
return PROGRAM_ID_ENTRIES.iter().find(|entry| {
return entry.program_id() == program_id;
});
}
/// Finds one registered Program ID by its typed Solana representation.
#[must_use]
pub fn find_program_pubkey(program_id: &crate::Pubkey) -> std::option::Option<&'static crate::ProgramIdEntry> {
return PROGRAM_ID_ENTRIES.iter().find(|entry| {
return entry.pubkey() == *program_id;
});
}
#[cfg(test)]
#[path = "../unit_tests/program_ids.rs"]
mod tests;

View File

@@ -0,0 +1,67 @@
// file: crates/ksp-core-lib/tests/public_api.rs
// version: 4
//! Integration tests for the public `ksp-core-lib` contracts.
ksp_core_lib::declare_program_id!(TEST_PRGID_SYSTEM, TEST_PRGIDPK_SYSTEM, "11111111111111111111111111111111");
const TEST_ERROR_CODE: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("consumer", "failed");
fn public_result() -> ksp_core_lib::Result<()> {
let error = ksp_core_lib::Error::new(TEST_ERROR_CODE, "consumer failure").with_context("operation", "public_api");
return std::result::Result::Err(error);
}
#[test]
fn error_contract_is_consumable_from_crate_root() {
let result = public_result();
assert!(result.is_err());
let error = match result {
std::result::Result::Ok(()) => return,
std::result::Result::Err(error) => error,
};
assert_eq!(error.code(), TEST_ERROR_CODE);
assert_eq!(error.message(), "consumer failure");
assert_eq!(error.context(), &[ksp_core_lib::ErrorContext::new("operation", "public_api")]);
assert_eq!(std::string::ToString::to_string(&error), "consumer.failed: consumer failure");
return;
}
#[test]
fn program_id_contract_is_consumable_from_crate_root() {
assert_eq!(TEST_PRGID_SYSTEM, ksp_core_lib::PRGID_SOLANA_SYSTEM);
assert_eq!(TEST_PRGIDPK_SYSTEM, ksp_core_lib::PRGIDPK_SOLANA_SYSTEM);
assert_eq!(ksp_core_lib::entries().len(), 18);
assert_eq!(ksp_core_lib::native_program_ids().count(), 18);
let system = ksp_core_lib::find_program_id(ksp_core_lib::PRGID_SOLANA_SYSTEM);
let system = match system {
std::option::Option::Some(entry) => entry,
std::option::Option::None => return,
};
assert_eq!(system.code(), "solana.system");
assert_eq!(system.domain(), "solana");
assert_eq!(system.family(), "runtime");
assert_eq!(system.protocol(), "solana");
assert_eq!(system.pubkey(), ksp_core_lib::PRGIDPK_SOLANA_SYSTEM);
return;
}
#[test]
fn program_id_registry_supports_public_taxonomy_filters() {
let loader_count = ksp_core_lib::program_ids_by_family("loader").count();
let precompile_count = ksp_core_lib::program_ids_by_family("precompile").count();
let bpf_v2_count = ksp_core_lib::program_ids(
ksp_core_lib::ProgramIdFilter::new()
.with_domain("solana")
.with_family("loader")
.with_protocol("solana")
.with_subfamily("bpf")
.with_program_version("v2")
.with_kind(ksp_core_lib::ProgramIdKind::Loader),
)
.count();
assert_eq!(loader_count, 5);
assert_eq!(precompile_count, 3);
assert_eq!(bpf_v2_count, 1);
return;
}

View File

@@ -0,0 +1,78 @@
// file: crates/ksp-core-lib/unit_tests/error.rs
// version: 2
#[derive(Debug)]
struct TestSource;
impl std::fmt::Display for TestSource {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.write_str("source failure");
}
}
impl std::error::Error for TestSource {}
fn assert_send_sync<T>(_: std::marker::PhantomData<T>)
where
T: std::marker::Send + std::marker::Sync,
{
return;
}
#[test]
fn error_code_preserves_domain_and_code() {
const CODE: crate::ErrorCode = crate::ErrorCode::new("core", "sample_failure");
assert_eq!(CODE.domain(), "core");
assert_eq!(CODE.code(), "sample_failure");
return;
}
#[test]
fn error_context_preserves_key_and_value() {
let context = crate::ErrorContext::new("operation", "sample");
assert_eq!(context.key(), "operation");
assert_eq!(context.value(), "sample");
return;
}
#[test]
fn error_preserves_code_message_and_context_order() {
let code = crate::ErrorCode::new("core", "sample_failure");
let error = crate::Error::new(code, "sample message").with_context("first", "one").with_context("second", "two");
assert_eq!(error.code(), code);
assert_eq!(error.message(), "sample message");
assert_eq!(error.context().len(), 2);
assert_eq!(error.context()[0].key(), "first");
assert_eq!(error.context()[0].value(), "one");
assert_eq!(error.context()[1].key(), "second");
assert_eq!(error.context()[1].value(), "two");
return;
}
#[test]
fn display_contains_only_qualified_code_and_message() {
let error = crate::Error::new(crate::ErrorCode::new("core", "sample_failure"), "sample message")
.with_context("secret_free_context", "not rendered")
.with_source(TestSource);
assert_eq!(std::string::ToString::to_string(&error), "core.sample_failure: sample message");
return;
}
#[test]
fn standard_source_is_preserved() {
let error = crate::Error::new(crate::ErrorCode::new("core", "sample_failure"), "sample message").with_source(TestSource);
let source = std::error::Error::source(&error);
assert!(source.is_some());
let source_message = match source {
std::option::Option::Some(value) => std::string::ToString::to_string(value),
std::option::Option::None => std::string::String::new(),
};
assert_eq!(source_message, "source failure");
return;
}
#[test]
fn common_error_is_send_and_sync() {
assert_send_sync(std::marker::PhantomData::<crate::Error>);
return;
}

View File

@@ -0,0 +1,89 @@
// file: crates/ksp-core-lib/unit_tests/program_ids.rs
// version: 1
fn assert_program_id_types(program_id: &'static str, pubkey: crate::Pubkey) {
assert_eq!(pubkey, crate::Pubkey::from_str_const(program_id));
return;
}
#[test]
fn declared_program_ids_have_matching_text_and_pubkey_forms() {
assert_program_id_types(crate::PRGID_SOLANA_SYSTEM, crate::PRGIDPK_SOLANA_SYSTEM);
assert_program_id_types(crate::PRGID_SOLANA_STAKE, crate::PRGIDPK_SOLANA_STAKE);
assert_program_id_types(crate::PRGID_SOLANA_VOTE, crate::PRGIDPK_SOLANA_VOTE);
assert_program_id_types(crate::PRGID_SOLANA_SLASHING, crate::PRGIDPK_SOLANA_SLASHING);
return;
}
#[test]
fn registry_contains_the_eighteen_core_program_ids() {
assert_eq!(crate::entries().len(), 18);
assert_eq!(crate::native_program_ids().count(), 18);
return;
}
#[test]
fn registry_codes_program_ids_and_pubkeys_are_unique() {
for left_index in 0..crate::entries().len() {
for right_index in (left_index + 1)..crate::entries().len() {
let left = &crate::entries()[left_index];
let right = &crate::entries()[right_index];
assert_ne!(left.code(), right.code());
assert_ne!(left.program_id(), right.program_id());
assert_ne!(left.pubkey(), right.pubkey());
}
}
return;
}
#[test]
fn registry_pubkeys_match_their_owned_base58_values() {
for entry in crate::entries() {
assert_eq!(entry.pubkey(), crate::Pubkey::from_str_const(entry.program_id()));
}
return;
}
#[test]
fn filters_combine_domain_family_protocol_subfamily_version_and_kind() {
let bpf_v2 = crate::program_ids(
crate::ProgramIdFilter::new()
.with_domain("solana")
.with_family("loader")
.with_protocol("solana")
.with_subfamily("bpf")
.with_program_version("v2")
.with_kind(crate::ProgramIdKind::Loader),
)
.collect::<std::vec::Vec<_>>();
assert_eq!(bpf_v2.len(), 1);
assert_eq!(bpf_v2[0].program_id(), crate::PRGID_SOLANA_LOADER_BPF_V2);
return;
}
#[test]
fn family_views_cover_expected_core_groups() {
assert_eq!(crate::program_ids_by_family("runtime").count(), 5);
assert_eq!(crate::program_ids_by_family("consensus").count(), 3);
assert_eq!(crate::program_ids_by_family("loader").count(), 5);
assert_eq!(crate::program_ids_by_family("precompile").count(), 3);
assert_eq!(crate::program_ids_by_family("proof").count(), 2);
return;
}
#[test]
fn direct_lookup_supports_text_and_typed_program_ids() {
let by_text = crate::find_program_id(crate::PRGID_SOLANA_SLASHING);
let by_pubkey = crate::find_program_pubkey(&crate::PRGIDPK_SOLANA_SLASHING);
assert_eq!(by_text.map(crate::ProgramIdEntry::code), std::option::Option::Some("solana.slashing"));
assert_eq!(by_pubkey.map(crate::ProgramIdEntry::code), std::option::Option::Some("solana.slashing"));
return;
}
#[test]
fn non_program_well_known_accounts_are_absent() {
assert!(crate::find_program_id("1nc1nerator11111111111111111111111111111111").is_none());
assert!(crate::find_program_id("StakeConfig11111111111111111111111111111111").is_none());
assert!(crate::find_program_id("SysvarC1ock11111111111111111111111111111111").is_none());
return;
}

View File

@@ -0,0 +1,285 @@
<!-- file: deltas/0.1.1/pre.001-fix.001.md -->
<!-- version: 1 -->
# Delta 0.1.1-pre.001-fix.001
## Base requise
Livraison précédente appliquée et commitée :
```text
0.1.1-pre.001
```
La base stable initiale `khadhroony-solana-project-v0.0.3.zip` provient directement de Gitea depuis le tag `v0.0.3`. L'absence de `.git` dans cette archive n'impose donc aucune vérification supplémentaire du tag pour le cadrage de cette session.
## Type de livraison
```text
ksp-doc-0.1.1-pre.001-fix.001.zip
```
## Objectif
Corriger le plan de `0.1.1-pre.001` après validation du brainstorming Program IDs, sans ouvrir `pre.002` et sans modifier de code/runtime.
Ce correctif :
- confirme `solana-pubkey` comme dépendance Solana fondamentale candidate de Core ;
- interdit `solana-sdk-ids` comme dépendance KSP, y compris de développement ;
- fait posséder à KSP ses chaînes Base58 et représentations `Pubkey` de Program IDs ;
- fixe les préfixes `PRGID_` et `PRGIDPK_` ;
- fixe la structure générale de nomenclature `<PREFIX>_<DOMAIN>_<SUBDOMAIN?>_<NAME>_<VERSION?>` ;
- prévoit une macro KSP `declare_program_id!` produisant les deux représentations depuis une déclaration unique ;
- réintroduit et améliore le concept de registre descriptif enumerable inspiré de l'ancien `ks-program-ids` ;
- supprime le nombre arbitrairement figé de 17 Program IDs avant l'inventaire final de `pre.003` ;
- maintient la séparation stricte entre Program IDs et well-known accounts.
## Version Cargo
Aucun fichier participant au code, build, runtime, à la configuration exécutable ou aux migrations n'est modifié.
Conformément à `VER-ID-008`, `workspace.package.version` reste donc :
```text
0.1.1-pre.1
```
Le correctif possède néanmoins son identifiant de livraison/commit propre :
```text
0.1.1-pre.001-fix.001
```
## Fichiers ajoutés
- `deltas/0.1.1/pre.001-fix.001.md`
## Fichiers modifiés
- `docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md` — version documentaire 1 -> 2.
## Fichiers supprimés
Aucun.
## Corrections et décisions incorporées
### Provenance de la base stable
La réserve de `pre.001` liée à l'absence de `.git` dans l'archive Gitea est retirée du plan actif.
Dans le workflow KSP fourni, une archive nommée `khadhroony-solana-project-vX.Y.Z.zip` est produite directement par Gitea depuis le tag correspondant. `khadhroony-solana-project-v0.0.3.zip` est donc acceptée comme base stable/taguée `v0.0.3`.
Le delta `pre.001` déjà livré n'est pas réécrit ; ce correctif trace explicitement la correction.
### Dépendances Solana/Anza
Direction acquise :
```text
ksp-core-lib -> solana-pubkey
```
lorsque `pre.003` implémentera réellement la surface Program IDs.
En revanche :
```text
ksp-core-lib -X-> solana-sdk-ids
```
s'applique aux dépendances runtime **et** de développement.
`solana-sdk-ids` peut être consultée comme source officielle externe lors des audits, mais elle ne doit pas entrer dans le graphe Cargo KSP.
### Ownership et représentations des Program IDs
KSP possède la valeur Base58 canonique de chaque Program ID retenu.
Chaque ID expose deux représentations publiques liées :
```text
PRGID_<SUFFIXE> : &'static str
PRGIDPK_<SUFFIXE> : Pubkey
```
Le suffixe doit être strictement identique entre les deux formes.
La nomenclature générale est :
```text
<PREFIX>_<DOMAIN>_<SUBDOMAIN?>_<NAME>_<VERSION?>
```
Exemples de convention :
```text
PRGID_SOLANA_SYSTEM
PRGIDPK_SOLANA_SYSTEM
PRGID_SOLANA_LOADER_BPF_V2
PRGIDPK_SOLANA_LOADER_BPF_V2
PRGID_SOLANA_PRECOMPILE_ED25519
PRGIDPK_SOLANA_PRECOMPILE_ED25519
PRGID_SPL_MEMO_V3
PRGIDPK_SPL_MEMO_V3
```
L'exemple SPL Memo définit uniquement la convention future ; il n'ouvre pas SPL dans le périmètre fonctionnel de `0.1.1`.
### Macro de déclaration
Le plan prévoit une macro publique KSP initialement nommée :
```text
declare_program_id!
```
Elle doit prendre une seule valeur Base58 canonique et produire les deux constantes `PRGID_*` et `PRGIDPK_*` correspondantes à la compilation.
La macro doit s'inspirer de la mécanique compile-time de `solana_address::declare_id!`/des primitives accessibles via la génération retenue de `solana-pubkey`, tout en conservant une API KSP adaptée à plusieurs Program IDs dans la même crate.
Elle ne doit notamment pas imposer des symboles génériques `ID`, `id()` ou `check_id()` qui entreraient en collision entre plusieurs déclarations.
### Registre descriptif enumerable
Le rejet initial d'un `ProgramIdEntry` enumerable est annulé.
L'ancien `ks-program-ids` fournissait notamment :
```text
ProgramIdEntry
entries()
registered_program_ids()
native_program_ids()
native_well_known_account_ids()
find_registered_program_id()
```
La surface KSP doit reprendre/améliorer les capacités utiles sans reprendre les redondances historiques.
Direction de `pre.003` :
```text
ProgramIdEntry
entries()
native_program_ids()
find_program_id()
```
Une recherche typée par `Pubkey` reste autorisée si son utilité est démontrée pendant l'implémentation.
`registered_program_ids()` n'est pas repris automatiquement s'il ne fait que dupliquer `entries()`.
`ProgramIdEntry` doit pouvoir exposer au minimum un code KSP stable, les formes `PRGID_*`/`PRGIDPK_*` et une classification descriptive minimale permettant les sous-ensembles utiles sans dupliquer plusieurs registres.
### Program IDs fondamentaux
La liste de `pre.001` n'est plus figée à 17 entrées.
L'inventaire final sera confirmé dans `pre.003` contre les sources officielles actuelles, en couvrant notamment :
- System, Stake, Vote, Config, Feature et Compute Budget ;
- Address Lookup Table ;
- loaders BPF historiques/actuels, Loader v4 et Native Loader ;
- précompiles Ed25519, Secp256k1 et Secp256r1 ;
- programmes ZK fondamentaux encore pertinents ;
- toute surface native/historique supplémentaire réellement justifiée.
L'ancien `ks-program-ids` reste un inventaire historique utile. Son entrée `slashing` doit par exemple être réévaluée selon son statut officiel actuel plutôt que retenue ou rejetée uniquement parce qu'elle figurait dans bot3.
### Program IDs et well-known accounts
Les Program IDs exécutables et les well-known account IDs restent deux concepts distincts.
`PRGID_*` / `PRGIDPK_*` ne doivent jamais nommer un compte connu non exécutable.
Le concept historique `native_well_known_account_ids()` est conservé comme direction architecturale possible, mais `0.1.1` ne crée pas une API vide pour ce domaine si aucun well-known account n'est retenu dans sa surface réelle.
## Impact sur les prereleases suivantes
`pre.002` ne change pas :
```text
Error / Result
```
`pre.003` est précisé :
```text
Pubkey
+ declare_program_id!
+ PRGID_* / PRGIDPK_*
+ inventaire final des Program IDs fondamentaux
+ ProgramIdEntry / entries() / native_program_ids() / find_program_id()
+ tests de conformité/unicité
```
Aucune dépendance `solana-sdk-ids` ne doit y être ajoutée.
## Hors scope inchangé
Le correctif n'ouvre toujours pas :
- Logging ;
- Config ;
- Tauri ;
- Wallet/signing ;
- codecs wire ;
- Interface ;
- Program decoding/dispatch registry/`ProgramExecutionPreparer` ;
- execution policy/orchestration ;
- Transport ;
- Store ;
- Materializer ;
- workers/jobs/pipelines ;
- scenarios ;
- trading/ML.
## Validations exécutées
- relecture des règles `VERSION_WORKFLOW.md` et `FILE_CONTRACTS.md` de la base stable ;
- confirmation qu'un fix purement documentaire ne modifie pas la version Cargo ;
- réaudit ciblé de l'ancien `ks-program-ids` fourni dans l'archive bot3 de référence : `ProgramIdEntry`, `entries()`, `registered_program_ids()`, `native_program_ids()`, `native_well_known_account_ids()` et `find_registered_program_id()` ;
- relecture du plan `003-V0_1_1_CORE_FOUNDATION_PLAN.md` après correction ;
- contrôle statique du header/version des fichiers livrés ;
- contrôle des fins de fichiers ;
- contrôle de la structure et du contenu de l'archive ;
- vérification de l'absence de fichier Cargo/code/runtime dans ce correctif.
## Validations non exécutées
Aucune validation Cargo n'est déclarée pour ce correctif documentaire.
Les commandes suivantes ne sont pas nécessaires pour démontrer le contenu de ce delta, qui ne modifie aucun artefact compilé :
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
```
Elles restent les validations attendues dès la prochaine tranche Rust applicable.
## Questions ouvertes
Les décisions nécessaires pour quitter `pre.001` sont considérées validées.
Les points d'implémentation suivants sont volontairement reportés à leur tranche propriétaire sans bloquer `pre.002` :
- structure Rust exacte et classification minimale de `ProgramIdEntry` en `pre.003` ;
- présence éventuelle d'une recherche dédiée par `Pubkey` ;
- inventaire final des IDs natifs/historiques à partir des sources officielles actuelles ;
- détail d'expansion de `declare_program_id!` selon l'API exacte de la version `solana-pubkey` retenue.
## Suite
Après validation/commit de ce correctif :
```text
0.1.1-pre.002 — Error/Result et fondation API
```

View File

@@ -0,0 +1,277 @@
<!-- file: deltas/0.1.1/pre.001-fix.002.md -->
<!-- version: 1 -->
# Delta 0.1.1-pre.001-fix.002
## Base requise
Livraison précédente appliquée et commitée :
```text
0.1.1-pre.001-fix.001
```
## Type de livraison
```text
ksp-doc-0.1.1-pre.001-fix.002.zip
```
## Objectif
Compléter le cadrage Program IDs de `pre.001` avant ouverture de `pre.002`, après réaudit de l'ancien registre/IDLs bot3 et confrontation à des Program IDs publics actuels.
Ce correctif reste purement documentaire. Il fixe l'architecture de classification/recherche nécessaire pour que le registre KSP puisse ultérieurement retrouver les programmes par domaine, famille, protocole, sous-famille et génération sans dupliquer plusieurs registres statiques.
## Version Cargo
Aucun fichier de code/build/runtime n'est modifié.
`workspace.package.version` reste donc :
```text
0.1.1-pre.1
```
Identifiant de livraison/commit :
```text
0.1.1-pre.001-fix.002
```
## Fichiers ajoutés
- `deltas/0.1.1/pre.001-fix.002.md`
## Fichiers modifiés
- `docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md` — version documentaire 2 -> 3.
## Décisions acquises
### Registre canonique unique
KSP ne doit pas maintenir des tableaux indépendants pour chaque vue (`native`, `amm`, protocole, etc.).
`entries()` reste la source canonique et les vues/recherches sont dérivées de la classification de chaque `ProgramIdEntry`.
### Taxonomie minimale
`ProgramIdEntry` doit être conçu pour porter au minimum les axes descriptifs suivants :
```text
domain
family
protocol
subfamily?
program_version?
kind
```
auxquels s'ajoutent le code KSP unique, la chaîne Base58 `PRGID_*` et le `Pubkey` `PRGIDPK_*`.
Les vocabulaires de classification restent extensibles. Core ne doit pas posséder une enum fermée de tous les protocoles/familles futurs.
### AMM comme famille agrégatrice
Le réaudit de bot3 montre que les anciens préfixes `AMM`, `CPMM`, `CLMM`, `DLMM`, `STABLE_SWAP` et `WEIGHTED_SWAP` représentent plusieurs spécialisations d'une même grande famille utile pour la recherche.
La direction KSP devient donc :
```text
family = amm
```
avec des `subfamily` optionnelles telles que :
```text
cpmm
clmm
dlmm
damm
stable_swap
weighted_swap
gamma
ssl
```
lorsqu'elles correspondent réellement à une branche/architecture reconnue.
Une future `amm_program_ids()` doit filtrer le registre canonique sur cette famille et inclure toutes ces sous-familles.
### `subfamily` et `program_version` sont deux axes indépendants
La version ne doit pas être détournée en sous-famille.
Cas audités :
- SPL Memo : trois Program IDs v1/v3/v4, même lignée fonctionnelle ;
- Aldrin AMM : v1/v2, même famille/protocole ;
- Meteora DAMM : sous-famille `damm`, versions v1/v2 ;
- Meteora DLMM : sous-famille `dlmm` distincte de DAMM ;
- Jupiter Aggregator : même lignée, versions v4/v6 ;
- GooseFX : GAMMA et SSL sont des branches distinctes ; `SSL v2` possède une version, tandis qu'aucun `v1` ne doit être inventé pour GAMMA.
### Nom du champ de version
Le champ retenu conceptuellement est :
```text
program_version
```
et non `protocol_version`.
Raison : un protocole peut posséder simultanément plusieurs programmes/composants dont les versions évoluent indépendamment. La version doit être attachée à la lignée du Program ID concerné, pas au protocole entier.
`program_version` est un label de génération reconnu (`v1`, `v2`, `v4`, `v6`, `v0.5`, etc.), pas nécessairement un SemVer.
### Version d'IDL explicitement distincte
Une version d'IDL/schema n'est pas une version de Program ID.
Exemples observés dans les IDLs archivées bot3 :
- Jupiter V6 : IDL `0.1.0` ;
- GooseFX SSL V2 : IDL `0.3.0` ;
- GooseFX GAMMA : IDL/schema `0.2.0` ;
- Meteora DAMM V2 : IDL/schema distinct de la génération publique `v2`.
La provenance/version des IDLs appartiendra à la couche Interface/decoder lorsqu'elle sera ouverte ; elle n'entre pas dans `ProgramIdEntry` de Core en `0.1.1`.
### Nomenclature des constantes
La nomenclature Rust est séparée de la taxonomie fonctionnelle.
Le premier segment après `PRGID_`/`PRGIDPK_` est désormais nommé `NAMESPACE`, afin de ne pas confondre le nom public stable avec le champ taxonomique `domain` :
```text
PRGID_<NAMESPACE>_<PROGRAM_OR_FAMILY>_<VARIANT?>_<VERSION?>
PRGIDPK_<NAMESPACE>_<PROGRAM_OR_FAMILY>_<VARIANT?>_<VERSION?>
```
Exemples :
```text
PRGID_SPL_MEMO_V3
PRGIDPK_SPL_MEMO_V3
PRGID_METEORA_DAMM_V2
PRGIDPK_METEORA_DAMM_V2
PRGID_GOOSEFX_GAMMA
PRGIDPK_GOOSEFX_GAMMA
PRGID_GOOSEFX_SSL_V2
PRGIDPK_GOOSEFX_SSL_V2
```
Le symbole public ne doit pas être renommé uniquement parce qu'une classification fonctionnelle est affinée plus tard.
### Recherches et vues prévues
Direction conceptuelle :
```text
ProgramIdEntry
ProgramIdFilter
entries()
program_ids(filter)
native_program_ids()
find_program_id()
```
Le filtre doit pouvoir combiner plusieurs critères.
Des helpers de recherche peuvent être proposés :
```text
program_ids_by_domain(...)
program_ids_by_family(...)
program_ids_by_protocol(...)
```
`native_program_ids()` reste une vue Core réelle de `0.1.1`.
`amm_program_ids()` est explicitement prévue pour la surface future possédant des IDs AMM, mais n'est pas créée vide pendant `0.1.1` puisque les AMM sont hors scope fonctionnel de la release.
Les vues doivent pouvoir être des iterators/views du registre canonique afin d'éviter la duplication de tableaux statiques.
## Réaudit effectué
### Archive bot3
Le réaudit a porté sur :
- `ks-program-ids` et ses 137 entrées historiques ;
- `ProgramIdEntry`, `entries()`, `native_program_ids()` et la recherche historique ;
- les familles historiques AMM/CLMM/CPMM/DLMM/router/orderbook/etc. ;
- les IDLs archivées, notamment GooseFX GAMMA/V2, Meteora DAMM/DLMM, Jupiter v4/v6, CCTP v1/v2, Raydium CLMM/CPMM, OpenBook v2 et Marginfi v2.
L'inventaire montre également qu'un même protocole traverse plusieurs familles : Jupiter, Raydium, Meteora, Kamino, MetaDAO, Pump, Metaplex, Orca, etc. `protocol` doit donc être un axe séparé de `family`.
### Sources externes actuelles
Contrôles représentatifs effectués le 2026-08-14 :
- Agave runtime `fetch-spl.sh` : Memo 1.0.0, 3.0.0 et 4.0.0 sont associés à trois Program IDs distincts ;
- `spl-memo-interface` actuel : modules `v1`, `v3`, `v4` distincts ;
- Solana Explorer et Solscan : présence/identification des Program IDs Memo et de programmes DEX audités ;
- GooseFX officiel : GAMMA est une lignée AMM distincte ; l'écosystème publie également la lignée SSL ;
- Meteora officiel : DAMM v1, DAMM v2 et DLMM sont des surfaces distinctes ;
- Jupiter officiel : plusieurs générations du Swap Aggregator sont distinguées par Program ID.
Ces contrôles sont utilisés comme validation de taxonomie, pas comme dépendances KSP.
## Impact sur `pre.003`
`pre.003` devra désormais :
- implémenter `ProgramIdEntry` avec une taxonomie compatible avec les axes acquis ;
- implémenter `ProgramIdFilter` ou une forme équivalente permettant les intersections ;
- dériver `native_program_ids()` du registre canonique ;
- tester les filtres par domaine/famille/protocole/sous-famille/version/kind ;
- ne jamais confondre génération du programme et version d'IDL ;
- préserver la possibilité d'ajouter plus tard `amm_program_ids()` sans modifier la structure fondamentale du registre.
## Hors scope inchangé
Ce correctif n'ajoute aucun Program ID SPL/DEX au code de `0.1.1` et n'ouvre toujours pas :
- Logging ;
- Config ;
- Tauri ;
- Wallet/signing ;
- codecs wire ;
- Interface/IDL runtime ;
- Program decoding/dispatch ;
- execution ;
- Transport ;
- Store ;
- Materializer ;
- workers/jobs/pipelines ;
- scenarios ;
- trading/ML.
## Validations exécutées
- réaudit du registre `ks-program-ids` de l'archive bot3 ;
- inventaire des Program IDs versionnés et des protocoles présents dans plusieurs familles ;
- inspection ciblée des IDLs multi-version/à plusieurs sous-familles ;
- confrontation représentative avec Agave/SPL, Solana Explorer, Solscan, GooseFX, Meteora et Jupiter ;
- relecture du plan après modification ;
- contrôle statique des headers/version documentaire ;
- contrôle des fins de fichiers ;
- contrôle de l'absence de modification Cargo/code/runtime dans ce fix.
## Validations non exécutées
Aucune validation Cargo n'est requise pour ce fix exclusivement documentaire et aucune n'est déclarée réussie.
## Suite
Après application et commit de ce correctif, le cadrage `pre.001` peut être considéré comme clôturé et la session peut passer à :
```text
0.1.1-pre.002 — Error/Result et fondation API
```

219
deltas/0.1.1/pre.001.md Normal file
View File

@@ -0,0 +1,219 @@
<!-- file: deltas/0.1.1/pre.001.md -->
<!-- version: 1 -->
# Delta 0.1.1-pre.001
## Base requise
Release stable/taguée attendue :
```text
v0.0.3
```
L'archive de base fournie contient bien la version Cargo stable `0.0.3` et le delta final `deltas/0.0.3/rel.001.md`.
Elle ne contient pas `.git` : le working tree réel, le commit et le tag `v0.0.3` doivent être vérifiés sur le dépôt cible avant commit de ce delta.
## Objectif
Ouvrir `0.1.1` par la prerelease obligatoire de brainstorming, audit et planification, sans développement fonctionnel Core.
Cette tranche :
- inventorie la surface réelle de `ksp-core-lib` ;
- borne les contrats N1 de la release ;
- propose le contrat ouvert `Error` / `Result` ;
- borne les Program IDs fondamentaux ;
- audite les primitives Solana/Anza actuelles nécessaires ;
- fixe la stratégie d'API, tests et dépendances ;
- dimensionne `pre.002` à `pre.005` ;
- confirme les hors-scope.
## Version Cargo
`workspace.package.version` passe de :
```text
0.0.3
```
à :
```text
0.1.1-pre.1
```
L'identifiant Cargo respecte SemVer sans zéro initial ; l'identifiant de livraison reste `0.1.1-pre.001`.
Le header de `Cargo.toml` passe de version 17 à 18.
## Fichiers ajoutés
- `docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md`
- `deltas/0.1.1/pre.001.md`
## Fichiers modifiés
- `Cargo.toml`
- `ROADMAP.md`
- `docs/plans/000-README.md`
## Fichiers supprimés
Aucun.
## Inventaire Core
`ksp-core-lib` est encore un squelette volontairement minimal :
- aucun module fonctionnel ;
- aucune dépendance externe ;
- aucun type public autre que la documentation de crate ;
- aucun test ;
- aucune surface Error/Result ou Program IDs existante à préserver pour compatibilité.
Cette situation permet de définir le contrat sans dette de compatibilité interne KSP.
## Décisions de planification
### Error/Result
Le modèle historique bot3 avec enum centrale de domaines n'est pas migré.
La direction retenue pour `pre.002` est :
- `Error` structuré ;
- `ErrorCode` ouvert avec domaine/code statiques ;
- `ErrorContext` structuré ;
- message lisible ;
- cause standard optionnelle `Send + Sync` ;
- alias `Result<T>` ;
- aucune connaissance dans Core des futurs domaines Config/Logging/Wallet/Transport/Store/Tauri/protocoles ;
- aucune liste centrale de conversions d'erreurs externes.
Le détail complet et les invariants figurent dans `docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md`.
### Solana/Anza
Sources officielles consultées le 2026-08-14 : dépôt `anza-xyz/solana-sdk`, notamment la crate Pubkey, la primitive Address, `sdk-ids` et le manifest workspace.
État vérifié :
```text
solana-pubkey 4.3.0
solana-address 2.7.0
solana-sdk-ids 3.1.0 package
Solana SDK workspace MSRV 1.89.0
```
Direction retenue :
- runtime Core : `solana-pubkey` seulement, lorsque `pre.003` implémentera réellement les Program IDs ;
- `default-features = false` tant qu'aucune feature supplémentaire n'est démontrée nécessaire ;
- `Pubkey` réexporté depuis `ksp_core_lib` ;
- pas de dépendance directe KSP à `solana-address` ;
- `solana-sdk-ids` seulement comme dev-dependency candidate pour les tests de conformité, pas comme dépendance runtime ;
- aucune autre primitive Solana autorisée n'est ajoutée par anticipation.
Les versions seront revérifiées juste avant leur ajout réel au manifeste.
### Program IDs
Première surface proposée : 17 Program IDs fondamentaux exposés par la source officielle Solana SDK, incluant les loaders et précompiles de la frontière runtime :
- Address Lookup Table ;
- BPF Loader ;
- BPF Loader deprecated ;
- BPF Loader Upgradeable ;
- Compute Budget ;
- Config ;
- Ed25519 precompile ;
- Feature ;
- Loader v4 ;
- Native Loader ;
- Secp256k1 precompile ;
- Secp256r1 precompile ;
- Stake ;
- System ;
- Vote ;
- ZK ElGamal Proof ;
- ZK Token Proof.
Sont exclus : sysvars, incinerator, stake config account, SPL/protocoles, registre enumerable et alias bot3 historiques.
### Autres primitives
Aucune autre primitive commune n'est justifiée maintenant.
`Hash`, `Nonce`, Keypair, Signer, identité/version de module et provenance restent reportés jusqu'à un besoin concret.
## Prereleases prévues
```text
pre.001 audit + brainstorming + plan
pre.002 Error/Result + tests publics
pre.003 Pubkey + Program IDs + conformité Solana
pre.004 intégration Core + audits + compléments strictement justifiés
pre.005 validations finales + docs/cleanup + prompt 0.1.2
```
Le découpage reste souple ; une tranche trop large sera scindée plutôt que surchargée.
## Hors scope confirmé
- Logging ;
- Config ;
- Tauri ;
- Wallet/signing ;
- codecs wire ;
- Interface ;
- Program decoding/registry/preparation ;
- Execution ;
- Transport ;
- Store ;
- Materializer ;
- workers/jobs/pipelines ;
- scenarios ;
- trading/ML.
## Validations exécutées
Dans l'environnement de préparation de ce delta :
- lecture/audit de l'archive complète `0.0.3` fournie ;
- vérification de la version stable `0.0.3` dans le manifest ;
- vérification de la présence du delta `0.0.3/rel.001` et du prompt final `0.1.1` ;
- inventaire de `ksp-core-lib` ;
- lecture des règles, plans et documents d'architecture requis par le prompt ;
- audit de l'ancien `ks-core` / `ks-program-ids` de l'archive bot3 fournie comme référence historique, sans le traiter comme source de vérité KSP ;
- vérification des versions et surfaces actuelles sur les sources officielles Anza/Solana ;
- parsing TOML statique du manifest modifié ;
- contrôle statique des headers `file:` / `version:` des fichiers ajoutés/modifiés ;
- contrôle statique des liens Markdown locaux après modification.
## Validations non exécutées
L'environnement de préparation ne contient ni `cargo` ni `rustc`.
Les commandes suivantes n'ont donc pas pu être exécutées ici :
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
```
Elles doivent être exécutées sur le dépôt réel après application du delta. Aucun succès Cargo n'est déclaré par ce delta.
Le tag Git et le working tree ne peuvent pas non plus être vérifiés depuis l'archive fournie, qui ne contient pas `.git`.
## Questions ouvertes
- validation par le user du modèle Error/Result proposé avant `pre.002` ;
- choix exact des méthodes ergonomiques de contexte et du format `Display`, à stabiliser par tests en `pre.002` ;
- revérification de la version Solana et du MSRV juste avant `pre.003` ;
- confirmation de l'utilité de `solana-sdk-ids` comme dev-dependency de conformité au moment où les tests sont écrits.
Aucune question ouverte ne justifie de commencer le développement fonctionnel avant validation de ce plan.

View File

@@ -0,0 +1,127 @@
<!-- file: deltas/0.1.1/pre.002-fix.001.md -->
<!-- version: 1 -->
# Delta 0.1.1-pre.002-fix.001
## Base requise
Commit de livraison attendu :
```text
v0.1.1-pre.002
```
La version Cargo reste :
```text
0.1.1-pre.2
```
Ce correctif ne modifie aucun contrat public de `ksp-core-lib` et ne justifie donc aucune nouvelle prerelease Cargo.
## Objectif
Corriger les deux warnings remontés par les validations réelles de `0.1.1-pre.002` avant d'ouvrir `0.1.1-pre.003`.
Les validations exécutées sur le dépôt cible ont confirmé que :
- `cargo fmt --all` réussit ;
- `cargo check --workspace` réussit ;
- `cargo test --workspace` réussit avec 6 tests unitaires, 1 test d'intégration et 0 échec ;
- `cargo clippy --workspace --all-targets` termine sans erreur mais remonte deux warnings à corriger.
Warnings observés :
1. `clippy::extra_unused_type_parameters` sur le helper de vérification `Send + Sync` ;
2. `missing_docs` sur la crate de test d'intégration `tests/public_api.rs`.
## Fichiers modifiés
- `crates/ksp-core-lib/unit_tests/error.rs`
- `crates/ksp-core-lib/tests/public_api.rs`
## Fichier ajouté
- `deltas/0.1.1/pre.002-fix.001.md`
## Fichiers supprimés
Aucun.
## Correction `Send + Sync`
Le helper de test :
```text
assert_send_sync<T>()
```
utilisait `T` uniquement dans ses bornes de trait. Clippy considère alors le paramètre de type comme inutilisé avec `extra_unused_type_parameters`.
Le helper reçoit désormais un `std::marker::PhantomData<T>` :
```text
assert_send_sync<T>(PhantomData<T>)
```
et le test fournit `PhantomData<crate::Error>`.
Cette forme conserve exactement l'objectif du test de compilation : l'appel ne compile que si `crate::Error` satisfait `Send + Sync`, tout en utilisant réellement le paramètre générique et sans ajouter de dépendance, d'import ou de logique runtime significative.
## Correction `missing_docs`
Le test d'intégration `crates/ksp-core-lib/tests/public_api.rs` constitue une crate Rust indépendante lors de sa compilation.
Une rustdoc crate-level est ajoutée :
```text
//! Integration tests for the public `ksp-core-lib` error contract.
```
Cela satisfait le lint workspace `missing_docs = warn` sans désactiver le lint et sans documenter artificiellement les helpers privés du test.
## Headers de fichiers
Les deux fichiers Rust modifiés passent de :
```text
version: 1
```
à :
```text
version: 2
```
## Contrat public
Aucun changement.
Les éléments suivants restent strictement identiques à `0.1.1-pre.002` :
```text
ksp_core_lib::Error
ksp_core_lib::ErrorCode
ksp_core_lib::ErrorContext
ksp_core_lib::Result<T>
```
Le plan `docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md` reste en version documentaire 5 : aucune décision d'architecture ou d'API n'est modifiée par ce correctif.
## Dépendances
Aucune dépendance ajoutée ou modifiée.
## Validations à exécuter après application
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
```
Le résultat attendu de ce correctif est l'absence des deux warnings qui ont motivé `pre.002-fix.001`.
Aucune validation du correctif lui-même n'est déclarée réussie tant que ces commandes n'ont pas été exécutées sur le dépôt cible.

215
deltas/0.1.1/pre.002.md Normal file
View File

@@ -0,0 +1,215 @@
<!-- file: deltas/0.1.1/pre.002.md -->
<!-- version: 1 -->
# Delta 0.1.1-pre.002
## Base requise
Commit de livraison attendu :
```text
v0.1.1-pre.001-fix.002
```
Le plan actif est `docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md` version documentaire 4, incluant le réalignement du tableau des cas représentatifs effectué avant le commit du correctif précédent.
## Objectif
Implémenter la première surface fonctionnelle de `ksp-core-lib` : le contrat commun ouvert `Error` / `Result` validé pendant `pre.001`.
Cette tranche reste strictement bornée à l'erreur commune et n'ouvre aucune dépendance Solana ni aucun Program ID.
## Version Cargo
`workspace.package.version` passe de :
```text
0.1.1-pre.1
```
à :
```text
0.1.1-pre.2
```
L'identifiant Cargo respecte SemVer sans zéro initial ; l'identifiant de livraison reste `0.1.1-pre.002`.
Le header de `Cargo.toml` passe de version 18 à 19.
## Fichiers ajoutés
- `crates/ksp-core-lib/src/error.rs`
- `crates/ksp-core-lib/unit_tests/error.rs`
- `crates/ksp-core-lib/tests/public_api.rs`
- `deltas/0.1.1/pre.002.md`
## Fichiers modifiés
- `Cargo.toml`
- `crates/ksp-core-lib/src/lib.rs`
- `docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md`
## Fichiers supprimés
Aucun.
## Contrat implémenté
### `ErrorCode`
`ErrorCode` contient uniquement :
```text
domain: &'static str
code: &'static str
```
Décisions :
- `ErrorCode::new(...)` est `const` afin que chaque crate supérieure puisse définir ses propres codes statiques ;
- Core ne possède aucune enum centrale des domaines ;
- `domain()` et `code()` exposent les deux identifiants stables ;
- `ErrorCode` est `Copy`, `Clone`, `Eq`, `PartialEq`, `Hash` et `Debug` parce qu'il ne contient que deux chaînes statiques.
### `ErrorContext`
`ErrorContext` contient :
```text
key: &'static str
value: String
```
Le contexte conserve son ordre d'insertion dans `Error`.
L'API publique expose `ErrorContext::new(...)`, `key()` et `value()`.
### `Error`
`Error` contient :
```text
code: ErrorCode
message: String
context: Vec<ErrorContext>
source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>
```
Décisions stabilisées :
- `Error::new(...)` construit l'erreur minimale ;
- `with_context(...)` consomme `self`, ajoute un champ puis retourne l'erreur enrichie ;
- `with_source(...)` suit le même modèle pour une cause externe ;
- aucune méthode mutable publique parallèle n'est ajoutée ;
- aucune conversion générique `From<ExternalError>` n'est introduite ;
- `Error` ne dérive pas `Clone`, `Eq` ou `PartialEq`, afin de ne pas affaiblir le support d'une vraie cause externe ;
- `Error` implémente `std::fmt::Display` et `std::error::Error` ;
- le rendu `Display` est exactement :
```text
<domain>.<code>: <message>
```
Le contexte et la chaîne de causes ne sont pas injectés automatiquement dans ce rendu.
### `Result<T>`
La façade expose :
```text
ksp_core_lib::Result<T> = std::result::Result<T, ksp_core_lib::Error>
```
## Façade Core
`crates/ksp-core-lib/src/lib.rs` ouvre le module d'implémentation en privé puis réexporte explicitement :
```text
ksp_core_lib::Error
ksp_core_lib::ErrorCode
ksp_core_lib::ErrorContext
ksp_core_lib::Result
```
Aucun `pub mod` n'est introduit.
## Dépendances
Aucune dépendance n'est ajoutée à `ksp-core-lib` pendant cette tranche.
En particulier, `pre.002` n'introduit ni `thiserror`, ni `anyhow`, ni crate Solana, ni codec wire.
## Tests ajoutés
### Tests unitaires externes
`crates/ksp-core-lib/unit_tests/error.rs` vérifie :
- conservation de `domain` et `code` ;
- possibilité de déclarer un `ErrorCode` constant ;
- conservation des champs `ErrorContext` ;
- conservation du message et de l'ordre du contexte ;
- rendu exact de `Display` ;
- absence du contexte et de la cause dans le rendu ;
- conservation de la cause via `std::error::Error::source()` ;
- propriété `Send + Sync` de l'erreur commune.
Le fichier est rattaché au module privé de production via `#[cfg(test)]` et `#[path = "../unit_tests/error.rs"]`.
### Test d'intégration
`crates/ksp-core-lib/tests/public_api.rs` consomme exclusivement la façade crate-root et vérifie que `Error`, `ErrorCode`, `ErrorContext` et `Result` sont utilisables depuis une crate externe.
## Documentation de plan
Le plan passe de version documentaire 4 à 5 afin de remplacer les deux questions désormais résolues par les décisions réellement implémentées :
- `with_context(...)` consomme `self` ;
- `Display` utilise la forme stable `<domain>.<code>: <message>`.
Les questions `pre.003` concernant `solana-pubkey` restent ouvertes et inchangées.
## Validations exécutées
Dans l'environnement de préparation :
- reconstruction de la base `0.1.1-pre.001-fix.002` depuis la release `0.0.3` et les deltas successifs ;
- prise en compte de la version 4 du plan fournie après réalignement manuel du tableau ;
- contrôle du périmètre des fichiers modifiés/ajoutés ;
- parsing TOML statique du manifest racine ;
- contrôle des headers `file:` / `version:` et des fins de ligne des fichiers livrés ;
- recherche statique des usages interdits `unsafe`, `unwrap`, `expect`, `panic` et opérateur `?` dans le code de production ajouté ;
- contrôle de l'absence de `use` dans le code Rust ajouté ;
- contrôle de l'absence de nouvelle dépendance Cargo.
## Validations non exécutées
L'environnement de préparation ne contient ni `cargo` ni `rustc`.
Les commandes suivantes n'ont donc pas pu être exécutées ici :
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
```
Elles doivent être exécutées sur le dépôt réel avant validation du commit. Aucun succès Cargo n'est déclaré par ce delta.
## Décisions prises
- Le contrat Error/Result ouvert de `pre.001` est retenu sans enum centrale de domaines.
- `ErrorCode::new(...)` est `const`.
- Les champs des types publics restent privés et sont accessibles par API explicite.
- Le contexte est ordonné et enrichi par consommation de `self`.
- La cause standard est conservée avec les bornes `Error + Send + Sync + 'static`.
- `Display` est volontairement court et stable ; Logging décidera plus tard comment exploiter contexte et causes.
- Core ne possède aucune conversion vers les erreurs des domaines supérieurs.
## Questions ouvertes
Aucune question bloquante pour `pre.002`.
Les questions relatives à `Pubkey`, aux Program IDs et à leur registre restent réservées à `0.1.1-pre.003` conformément au plan actif.

View File

@@ -0,0 +1,120 @@
<!-- file: deltas/0.1.1/pre.003-fix.001.md -->
<!-- version: 1 -->
# Delta `0.1.1-pre.003-fix.001` — Cargo workspace + Clippy
## Statut
Correctif de `0.1.1-pre.003` après application et validation partielle de la tranche par le user.
Version technique du correctif :
```text
workspace.package.version = "0.1.1-pre.3.fix.1"
```
Le périmètre fonctionnel de `pre.003` ne change pas.
## Base et validations reçues
Sur `0.1.1-pre.3`, le user a exécuté avec succès :
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo tree -p ksp-core-lib
cargo tree -p ksp-core-lib -d
```
Résultats communiqués :
- 14 tests unitaires passent ;
- 3 tests d'intégration `public_api` passent ;
- `cargo tree` résout `solana-pubkey 4.3.0` puis `solana-address 2.7.0` ;
- `cargo tree -d` ne rapporte aucun doublon.
`cargo clippy --workspace --all-targets` échoue sur trois closures de `program_ids.rs` à cause de la règle workspace `clippy::implicit_return = deny`.
## Correction Cargo
La déclaration directe suivante dans `crates/ksp-core-lib/Cargo.toml` est supprimée :
```toml
solana-pubkey = { version = "4.3.0", default-features = false }
```
La dépendance appartient désormais au manifeste workspace :
```toml
[workspace.dependencies]
solana-pubkey = { version = "^4.3", default-features = false }
```
La crate propriétaire la consomme uniquement par héritage :
```toml
[dependencies]
solana-pubkey.workspace = true
```
La contrainte `^4.3` exprime la génération compatible voulue par KSP ; le patch concret reste résolu par Cargo/lockfile.
## Règles ajoutées
`docs/rules/RULES_DEPENDENCIES.md` ajoute `DEP-CARGO-001` à `DEP-CARGO-005` afin de rendre obligatoire :
- la centralisation des dépendances externes sous `[workspace.dependencies]` ;
- l'usage de `.workspace = true` dans les crates membres ;
- la centralisation des contraintes de version et options communes ;
- la convention de contrainte caret `^M.m` pour une génération majeure/mineure compatible ;
- la distinction entre contrainte de manifeste et résolution concrète du lockfile.
## Correction Clippy
Les trois closures signalées utilisent maintenant un `return` explicite dans leur corps :
- filtre générique de `program_ids(...)` ;
- recherche texte `find_program_id(...)` ;
- recherche typée `find_program_pubkey(...)`.
Aucune signature ni sémantique de l'API publique ne change.
## Documentation
`docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md` est aligné sur la nouvelle règle Cargo et documente la contrainte workspace `^4.3` au lieu d'une déclaration locale `4.3.0`.
## Fichiers modifiés
```text
Cargo.toml
crates/ksp-core-lib/Cargo.toml
crates/ksp-core-lib/src/program_ids.rs
docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md
docs/rules/RULES_DEPENDENCIES.md
```
## Fichier ajouté
```text
deltas/0.1.1/pre.003-fix.001.md
```
## Validations de ce correctif
Non exécutées dans l'environnement de préparation du delta, qui ne fournit pas la toolchain Cargo/Rust du dépôt.
À exécuter après application :
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
cargo tree -p ksp-core-lib
cargo tree -p ksp-core-lib -d
```
## Hors scope
Ce correctif n'ajoute ni Program ID, ni dépendance, ni primitive Core supplémentaire et ne démarre pas `0.1.1-pre.004`.

284
deltas/0.1.1/pre.003.md Normal file
View File

@@ -0,0 +1,284 @@
<!-- file: deltas/0.1.1/pre.003.md -->
<!-- version: 1 -->
# Delta `0.1.1-pre.003` — Pubkey + Program IDs
## Statut
Tranche fonctionnelle `0.1.1-pre.003` préparée après validation réussie par le user de `0.1.1-pre.002-fix.001`.
La base utilisateur observée avant ce delta utilise :
```text
workspace.package.version = "0.1.1-pre.2.fix.1"
```
Le présent delta ouvre :
```text
workspace.package.version = "0.1.1-pre.3"
```
## Validation de la base précédente
Le user a exécuté avec succès avant ce delta :
```bash
cargo fmt --all
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test --workspace
```
Les six tests unitaires Error et le test d'intégration public Error passent sans warning dans cette validation.
## Audit Solana/Anza revérifié
Au 2026-08-14 :
- `solana-pubkey 4.3.0` est la version publiée courante observée sur crates.io ;
- son MSRV publié est Rust `1.89.0` ;
- la validation précédente du user expose une génération Clippy `rust-1.94.0`, donc la toolchain observée satisfait ce MSRV ;
- `Pubkey` reste la façade de compatibilité officielle sur l'`Address` Solana actuel ;
- `Pubkey::from_str_const` permet le décodage Base58 compile-time nécessaire à la macro KSP ;
- `solana-sdk-ids` est consulté uniquement comme source d'audit et n'entre pas dans le graphe Cargo KSP.
Sources externes revérifiées pendant cette tranche :
- crates.io / docs.rs pour `solana-pubkey 4.3.0` ;
- `anza-xyz/solana-sdk`, `sdk-ids/src/lib.rs`, pour les identifiants fondamentaux actuellement publiés ;
- SIMD-0204 et la documentation Anza pour le Slashing Program `S1ashing11111111111111111111111111111111111`.
## Dépendance Core
`crates/ksp-core-lib/Cargo.toml` ajoute uniquement :
```toml
solana-pubkey = { version = "4.3.0", default-features = false }
```
Aucun codec wire, client RPC, umbrella SDK ou registre `solana-sdk-ids` n'est ajouté.
`ksp_core_lib::Pubkey` réexporte `solana_pubkey::Pubkey` depuis la façade Core.
## Macro KSP de Program ID
La macro publique :
```text
ksp_core_lib::declare_program_id!
```
possède la chaîne Base58 une seule fois et produit simultanément :
```text
PRGID_* : &'static str
PRGIDPK_* : Pubkey
```
La représentation typée est construite avec `Pubkey::from_str_const`, sans parsing runtime, `unwrap`, `expect`, `panic` ni opérateur `?`.
La macro ne génère pas de symboles génériques `ID`, `id()` ou `check_id()` et peut donc être utilisée plusieurs fois dans une même crate/module.
## Première surface Program IDs Core
La tranche fixe le premier registre à 18 Program IDs.
Les 17 valeurs de la surface officielle actuelle Anza `solana-sdk-ids` sont possédées localement par KSP :
1. Address Lookup Table ;
2. BPF Loader historique v1 ;
3. BPF Loader v2 ;
4. BPF Loader Upgradeable ;
5. Compute Budget ;
6. Config ;
7. Ed25519 precompile ;
8. Feature ;
9. Loader v4 ;
10. Native Loader ;
11. Secp256k1 precompile ;
12. Secp256r1 precompile ;
13. Stake ;
14. System ;
15. Vote ;
16. ZK ElGamal Proof ;
17. ZK Token Proof.
Le dix-huitième identifiant est :
```text
PRGID_SOLANA_SLASHING = "S1ashing11111111111111111111111111111111111"
```
Son statut de programme enshrined et son adresse sont confirmés séparément par SIMD-0204/Anza ; sa présence ne dépend donc pas de l'ancien registre bot3.
Les sysvars, `StakeConfig`, l'incinerator et les autres well-known accounts restent exclus du registre Program IDs.
## Nomenclature publique
Chaque entrée possède une paire `PRGID_*` / `PRGIDPK_*` au crate-root.
Exemples :
```text
PRGID_SOLANA_SYSTEM
PRGIDPK_SOLANA_SYSTEM
PRGID_SOLANA_LOADER_BPF_V1
PRGIDPK_SOLANA_LOADER_BPF_V1
PRGID_SOLANA_PRECOMPILE_ED25519
PRGIDPK_SOLANA_PRECOMPILE_ED25519
PRGID_SOLANA_SLASHING
PRGIDPK_SOLANA_SLASHING
```
Le suffixe est strictement identique entre les deux représentations.
## Registre canonique
`ProgramIdEntry` porte :
```text
code
name
program_id
pubkey
domain
family
protocol
subfamily
program_version
kind
```
Les axes fonctionnels restent extensibles sous forme de chaînes. Aucun enum central fermé des domaines, familles ou protocoles futurs n'est créé.
`ProgramIdKind` est limité à la classification technique :
```text
Program
Loader
Precompile
EnshrinedProgram
```
La première taxonomie Core utilise :
```text
domain = solana
protocol = solana
family = runtime
family = consensus
family = loader
family = precompile
family = proof
```
`program_version` reste distinct de `subfamily`. Les BPF loaders v1/v2 et Loader v4 utilisent l'axe version ; la branche BPF utilise séparément `subfamily = bpf`.
## API de recherche et vues
La façade expose :
```text
entries()
program_ids(filter)
native_program_ids()
program_ids_by_domain(...)
program_ids_by_family(...)
program_ids_by_protocol(...)
find_program_id(...)
find_program_pubkey(...)
```
`ProgramIdFilter` peut combiner :
```text
domain
family
protocol
subfamily
program_version
kind
```
Toutes les vues sont construites à partir du registre canonique unique. Elles retournent des iterators paresseux sans dupliquer un tableau statique par catégorie.
Une future fonction `amm_program_ids()` pourra donc devenir une vue `family = amm` sans changement structurel de `ProgramIdEntry`.
## Tests ajoutés
`unit_tests/program_ids.rs` vérifie notamment :
- cohérence texte/`Pubkey` des déclarations ;
- présence exacte des 18 Program IDs Core ;
- unicité des codes, Base58 et `Pubkey` ;
- correspondance de chaque `Pubkey` avec la Base58 KSP ;
- intersection des six axes de filtre ;
- tailles attendues des familles Core actuelles ;
- recherche texte et `Pubkey` ;
- absence de l'incinerator, `StakeConfig` et du Clock sysvar.
`tests/public_api.rs` vérifie en consommateur externe :
- la macro `declare_program_id!` ;
- les constantes `PRGID_*` / `PRGIDPK_*` ;
- `Pubkey` ;
- les vues du registre ;
- les getters de `ProgramIdEntry` ;
- le filtrage public combiné.
## Documentation
`docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md` passe en version documentaire 6 afin de :
- fixer l'inventaire Core à 18 IDs ;
- tracer la vérification du Slashing Program ;
- enregistrer `solana-pubkey 4.3.0` comme dépendance effectivement introduite ;
- fermer la question de MSRV de `pre.003` ;
- fixer `ProgramIdKind` et les retours iterator des vues ;
- compléter l'API publique réellement implémentée.
## Hors scope préservé
Cette tranche n'ajoute toujours pas :
- SPL Token/Token-2022/ATA/Memo ;
- Metaplex ou DEX ;
- well-known accounts ;
- codecs Borsh/Wincode ;
- decoder/executor/IDL ;
- RPC/WS ;
- Wallet ;
- Logging ;
- Config ;
- Store ;
- applications.
## Validations à exécuter sur le dépôt cible
Cette livraison ne déclare aucune validation Cargo non exécutée dans l'environnement de génération.
Après application :
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
cargo tree -p ksp-core-lib
cargo tree -p ksp-core-lib -d
```
Les deux commandes `cargo tree` doivent notamment confirmer l'absence de `solana-sdk-ids` et permettre de contrôler le graphe/features réellement résolus.
## Suite
Si les validations sont propres, la tranche suivante reste :
```text
0.1.1-pre.004 — intégration Core + audits
```

120
deltas/0.1.1/pre.004.md Normal file
View File

@@ -0,0 +1,120 @@
<!-- file: deltas/0.1.1/pre.004.md -->
<!-- version: 1 -->
# Delta `0.1.1-pre.004` — intégration Core + audits
## Statut
Tranche d'intégration préparée après validation réussie par le user de `0.1.1-pre.003-fix.001`.
La base validée utilise :
```text
workspace.package.version = "0.1.1-pre.3.fix.1"
```
Le présent delta ouvre :
```text
workspace.package.version = "0.1.1-pre.4"
```
## Validation de la base précédente
Le user a exécuté avec succès :
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
cargo tree -p ksp-core-lib
cargo tree -p ksp-core-lib -d
```
Résultats communiqués :
- 14 tests unitaires passent ;
- 3 tests d'intégration publics passent ;
- Clippy ne rapporte plus de warning ou d'erreur ;
- `cargo tree` résout `solana-pubkey 4.3.0` puis `solana-address 2.7.0` et leurs dépendances fondamentales ;
- `cargo tree -d` ne rapporte aucun doublon.
## Audit d'intégration Core
L'audit conjoint de `Error` / `Result`, `Pubkey` et Program IDs ne démontre aucun besoin de primitive N1 supplémentaire dans `0.1.1`.
La façade conserve les propriétés attendues :
- les modules d'implémentation restent privés ;
- les contrats consommables sont réexportés explicitement au crate-root ;
- `ksp-core-lib` ne dépend d'aucune couche KSP supérieure ;
- `solana-pubkey` reste l'unique dépendance externe directe de Core ;
- aucun codec wire, RPC/client, signer/keypair, store, logging ou configuration n'est introduit ;
- le registre Program IDs reste descriptif et distinct de tout registry de decoder/executor.
## Rustdocs
La rustdoc crate-level de `ksp-core-lib` est complétée afin de rendre explicites :
- le contrat d'erreur commun ;
- la propriété de `Pubkey` dans la façade Core ;
- la propriété KSP du registre de Program IDs fondamentaux ;
- l'absence de dépendance inverse vers les domaines supérieurs.
## Test public renforcé
`tests/public_api.rs` déclare désormais :
```text
const TEST_ERROR_CODE: ksp_core_lib::ErrorCode = ...
```
Le test confirme ainsi depuis une crate consommatrice que `ErrorCode::new(...)` est réellement utilisable en contexte `const`, ce qui permettra aux futures crates de domaine de posséder leurs codes sans faire connaître leurs domaines à Core.
Aucune signature publique n'est modifiée.
## Documentation du plan
`docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md` passe en version documentaire 8 pour enregistrer le résultat de l'audit `pre.004` et ajouter le contrôle explicite des features résolues :
```bash
cargo tree -p ksp-core-lib -e features
```
## Fichiers modifiés
```text
Cargo.toml
crates/ksp-core-lib/src/lib.rs
crates/ksp-core-lib/tests/public_api.rs
docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md
```
## Fichier ajouté
```text
deltas/0.1.1/pre.004.md
```
## Validations à exécuter sur le dépôt cible
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
cargo tree -p ksp-core-lib
cargo tree -p ksp-core-lib -d
cargo tree -p ksp-core-lib -e features
```
Aucune validation non exécutée dans l'environnement de préparation n'est déclarée réussie pour ce delta.
## Suite
Si cette tranche est propre, la suite prévue est :
```text
0.1.1-pre.005 — clôture, documentation finale et prompt 0.1.2
```

191
deltas/0.1.1/pre.005.md Normal file
View File

@@ -0,0 +1,191 @@
<!-- file: deltas/0.1.1/pre.005.md -->
<!-- version: 1 -->
# Delta `0.1.1-pre.005` — clôture Core et prompt Logging
## Base requise
`v0.1.1-pre.004` au sens du commit de livraison correspondant, avec :
```text
workspace.package.version = "0.1.1-pre.4"
```
Le présent delta ouvre :
```text
workspace.package.version = "0.1.1-pre.5"
```
## Objectif
Clôturer la phase de développement `0.1.1` sans élargir la surface Core : enregistrer les validations finales de `pre.004`, confirmer la politique de features `solana-pubkey`, réaligner les documents de référence et produire le prompt final de démarrage `0.1.2`.
La publication stable reste un delta `0.1.1-rel.001` séparé après validation de cette prerelease.
## Validation de la base précédente
Le user a exécuté avec succès le 2026-08-14 :
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
cargo tree -p ksp-core-lib
cargo tree -p ksp-core-lib -d
cargo tree -p ksp-core-lib -e features
```
Résultats communiqués :
- 14 tests unitaires passent ;
- 3 tests d'intégration publics passent ;
- Clippy passe sans warning communiqué ;
- `cargo tree` conserve `solana-pubkey 4.3.0` comme unique dépendance externe directe de `ksp-core-lib` ;
- `solana-pubkey` résout `solana-address 2.7.0` puis ses dépendances fondamentales ;
- `cargo tree -d` ne rapporte aucun doublon ;
- `cargo tree -e features` a été inspecté.
## Décision finale sur les features `solana-pubkey`
Aucune feature optionnelle supplémentaire n'est activée dans `0.1.1`.
La déclaration workspace reste :
```toml
solana-pubkey = { version = "^4.3", default-features = false }
```
Les features optionnelles `alloc`, `borsh`, `bytemuck`, `curve25519`, `rand`, `serde`, `sha2`, `std` et `wincode` ne correspondent à aucun besoin du contrat Core actuellement livré.
Les features `solana-address` visibles dans le graphe résolu (`copy`, `decode`, `error`, `sanitize`, `syscalls`, `default`) appartiennent à la composition interne de la génération actuelle de `solana-pubkey`/`solana-address`. Elles ne justifient pas une activation KSP supplémentaire.
Les futures releases activent une feature uniquement lorsque leur propriétaire fonctionnel démontre un besoin concret. En particulier, `borsh`/`wincode` ne sont pas activées dans Core par anticipation d'une future surface wire.
## Documentation finale
Le plan `0.1.1` est consolidé pour :
- refléter la surface réellement implémentée ;
- enregistrer les validations réussies de `pre.004` ;
- fermer la question des features `solana-pubkey` ;
- confirmer qu'aucune primitive N1 supplémentaire n'est nécessaire ;
- confirmer qu'aucun `README.md`/`USAGE.md` spécifique à la crate n'est nécessaire pour cette petite surface ;
- confirmer que le dépôt ne possède actuellement aucun changelog général à synchroniser.
La séquence fonctionnelle est mise à jour pour remplacer le périmètre candidat de `0.1.1` par la surface effectivement stabilisée et son lifecycle réellement suivi.
Les index de documentation/plans/prompts sont réalignés avec les fichiers présents.
## Prompt `0.1.2`
Ajout :
```text
prompts/002-V0_1_2_START_PROMPT.md
```
Ce prompt ouvre :
```text
0.1.2 — Logging foundation
```
après publication stable de `0.1.1`.
Il conserve notamment les décisions suivantes :
- `ksp-logging-lib` est la façade KSP unique de logging/tracing runtime ;
- Logging peut dépendre de `ksp-core-lib`, jamais l'inverse ;
- `pre.001` de Logging reste une phase d'audit/brainstorming/planification ;
- la stack `tracing` et ses features sont revérifiées depuis les sources officielles avant ajout ;
- l'API doit préserver les callsites réels ;
- Logging possède ses settings runtime sans dépendre de Config ;
- les secrets ne sont jamais loggés automatiquement ;
- les dépendances externes restent centralisées sous `[workspace.dependencies]`.
## Fichiers ajoutés
```text
deltas/0.1.1/pre.005.md
prompts/002-V0_1_2_START_PROMPT.md
```
## Fichiers modifiés
```text
Cargo.toml
docs/000-README.md
docs/plans/000-README.md
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md
prompts/000-README.md
```
## Fichiers supprimés
Aucun.
## Nettoyage/archivage
Aucun fichier temporaire ou obsolète supplémentaire n'est identifié comme devant être supprimé dans cette tranche.
Les deltas historiques et le prompt `0.1.1` restent conservés comme historique utile.
## Validations exécutées pendant la préparation
Contrôles statiques hors Cargo :
- parsing TOML ;
- headers `file:` / `version:` des fichiers modifiés/ajoutés ;
- terminaison EOF ;
- liens Markdown locaux ;
- cohérence des index documentaires ;
- cohérence de la version `0.1.1-pre.5` ;
- absence d'ajout de feature `solana-pubkey` ;
- intégrité de l'archive delta.
## Validations non exécutées pendant la préparation
L'environnement de préparation ne fournit pas `cargo`/`rustc`.
Après application du delta, exécuter sur le dépôt cible :
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
cargo tree -p ksp-core-lib
cargo tree -p ksp-core-lib -d
cargo tree -p ksp-core-lib -e features
```
Aucune de ces validations de `pre.005` n'est déclarée réussie avant exécution par le user.
## Publication suivante
Si `pre.005` est propre, préparer :
```text
0.1.1-rel.001
```
avec :
```text
workspace.package.version = "0.1.1"
```
Le commit de `rel.001` validé comme stable reçoit ensuite le tag :
```text
v0.1.1
```
La session suivante peut alors démarrer avec :
```text
prompts/002-V0_1_2_START_PROMPT.md
```

150
deltas/0.1.1/rel.001.md Normal file
View File

@@ -0,0 +1,150 @@
<!-- file: deltas/0.1.1/rel.001.md -->
<!-- version: 1 -->
# Delta `0.1.1-rel.001` — publication stable Core
## Base requise
`v0.1.1-pre.005` au sens du commit de livraison correspondant, avec :
```text
workspace.package.version = "0.1.1-pre.5"
```
## Objectif
Publier la release stable `0.1.1`, clôturer `Core foundation` et préparer l'ouverture de `0.1.2 — Logging foundation` sans modifier la surface fonctionnelle de `ksp-core-lib`.
## Version Cargo
`workspace.package.version` passe de :
```text
0.1.1-pre.5
```
à :
```text
0.1.1
```
Le header de `Cargo.toml` passe de version 24 à 25.
La politique de dépendance reste inchangée :
```toml
[workspace.dependencies]
solana-pubkey = { version = "^4.3", default-features = false }
```
Aucune feature optionnelle supplémentaire n'est activée.
## Validations finales exécutées par le user
Commandes exécutées avec succès le 2026-08-14 sur `0.1.1-pre.5` :
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
cargo tree -p ksp-core-lib
cargo tree -p ksp-core-lib -d
cargo tree -p ksp-core-lib -e features
```
Résultats communiqués :
- `cargo check --workspace` : succès ;
- `cargo test --workspace` : 14 tests unitaires réussis, 3 tests d'intégration publics réussis, doc-tests réussis ;
- `cargo clippy --workspace --all-targets` : succès sans warning communiqué ;
- `cargo tree -p ksp-core-lib` : dépendance externe directe unique `solana-pubkey 4.3.0`, résolvant `solana-address 2.7.0` ;
- `cargo tree -p ksp-core-lib -d` : aucun doublon ;
- `cargo tree -p ksp-core-lib -e features` : graphe inspecté, sans besoin d'activer une feature optionnelle `solana-pubkey` supplémentaire.
## Surface stable publiée
`0.1.1` stabilise notamment :
- `ksp_core_lib::ErrorCode`, `ErrorContext`, `Error` et `Result<T>` ;
- `ksp_core_lib::Pubkey` ;
- les 18 Program IDs fondamentaux possédés par KSP et leurs paires `PRGID_*` / `PRGIDPK_*` ;
- `declare_program_id!` ;
- `ProgramIdEntry`, `ProgramIdFilter`, `ProgramIdKind` ;
- le registre canonique enumerable/recherchable et ses vues par taxonomie ;
- `native_program_ids()`, recherches texte/`Pubkey` et filtres combinables ;
- la séparation `subfamily` / `program_version` ;
- les règles Cargo workspace introduites pendant la release.
Aucune nouvelle primitive ou API n'est ajoutée par le présent delta de publication.
## Documentation de clôture
Le présent delta :
- marque `0.1.1` réalisée dans `ROADMAP.md` ;
- conserve `003-V0_1_1_CORE_FOUNDATION_PLAN.md` comme plan historique clôturé ;
- réaligne la séquence fonctionnelle sur la publication stable ;
- réaligne les index de documentation/plans ;
- conserve `prompts/002-V0_1_2_START_PROMPT.md` comme prompt de démarrage de la release suivante.
Aucun changelog général n'existe dans la base actuelle ; aucun changelog artificiel n'est créé.
## Fichiers ajoutés
```text
deltas/0.1.1/rel.001.md
```
## Fichiers modifiés
```text
Cargo.toml
ROADMAP.md
docs/000-README.md
docs/plans/000-README.md
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md
```
## Fichiers supprimés
Aucun.
## Décisions
Aucune nouvelle décision architecturale.
La publication stable confirme les décisions et contrats stabilisés pendant les prereleases `0.1.1` et leurs fixes.
## Publication Git
Après application de ce delta :
1. vérifier que le working tree ne contient que les modifications attendues ;
2. exécuter au minimum un `cargo check --workspace` final sur la version Cargo stable `0.1.1` ;
3. créer le commit de release `v0.1.1-rel.001` ;
4. marquer ce commit comme release stable avec le tag :
```text
v0.1.1
```
Aucun autre tag n'est requis pour les prereleases/fixes historiques.
## Suite
Après le tag stable `v0.1.1`, ouvrir :
```text
0.1.2-pre.001
```
avec :
```text
prompts/002-V0_1_2_START_PROMPT.md
```
La première prerelease de `0.1.2` reste une phase de brainstorming, audit et planification avant développement fonctionnel de `ksp-logging-lib`.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/000-README.md --> <!-- file: docs/000-README.md -->
<!-- version: 8 --> <!-- version: 10 -->
# Documentation KSP # Documentation KSP
@@ -34,7 +34,8 @@ docs/
├── plans/ ├── plans/
│ ├── 000-README.md │ ├── 000-README.md
│ ├── 001-V0_0_3_PLAN.md │ ├── 001-V0_0_3_PLAN.md
── 002-FUNCTIONAL_RELEASE_SEQUENCE.md ── 002-FUNCTIONAL_RELEASE_SEQUENCE.md
│ └── 003-V0_1_1_CORE_FOUNDATION_PLAN.md
└── rules/ └── rules/
├── FILE_CONTRACTS.md ├── FILE_CONTRACTS.md
├── PROMPT_STRUCTURE.md ├── PROMPT_STRUCTURE.md
@@ -51,7 +52,7 @@ D'autres sous-répertoires seront ajoutés uniquement lorsque leur rôle aura é
## Documents de planification ## Documents de planification
Le plan historique de la phase fondatrice clôturée est conservé dans [`plans/001-V0_0_3_PLAN.md`](plans/001-V0_0_3_PLAN.md). La séquence active des premières releases fonctionnelles est définie dans [`plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md`](plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md). Le plan historique de la phase fondatrice clôturée est conservé dans [`plans/001-V0_0_3_PLAN.md`](plans/001-V0_0_3_PLAN.md). La séquence active des premières releases fonctionnelles est définie dans [`plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md`](plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md). Le plan détaillé de la release stable `0.1.1` est conservé comme historique clôturé dans [`plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md`](plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md).
`IDEAS.md` conserve les pistes et questions qui ne sont pas encore des engagements du roadmap ni des décisions architecturales. `IDEAS.md` conserve les pistes et questions qui ne sont pas encore des engagements du roadmap ni des décisions architecturales.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/000-README.md --> <!-- file: docs/plans/000-README.md -->
<!-- version: 3 --> <!-- version: 6 -->
# Plans KSP # Plans KSP
@@ -10,7 +10,8 @@ Un plan décrit le périmètre, les décisions déjà acquises, les questions ou
## Plans de référence ## Plans de référence
- [`001-V0_0_3_PLAN.md`](001-V0_0_3_PLAN.md) — plan historique de la phase fondatrice `0.0.3`, clôturée ; - [`001-V0_0_3_PLAN.md`](001-V0_0_3_PLAN.md) — plan historique de la phase fondatrice `0.0.3`, clôturée ;
- [`002-FUNCTIONAL_RELEASE_SEQUENCE.md`](002-FUNCTIONAL_RELEASE_SEQUENCE.md) — séquence active de référence des premières releases fonctionnelles, dont `0.1.1`. - [`002-FUNCTIONAL_RELEASE_SEQUENCE.md`](002-FUNCTIONAL_RELEASE_SEQUENCE.md) — séquence active de référence des premières releases fonctionnelles ;
- [`003-V0_1_1_CORE_FOUNDATION_PLAN.md`](003-V0_1_1_CORE_FOUNDATION_PLAN.md) — plan historique clôturé de la release stable `0.1.1`, établi par `0.1.1-pre.001` puis consolidé jusqu'à `0.1.1-rel.001`.
Le `pre.001` de chaque release fonctionnelle peut introduire son propre plan détaillé lorsque la release s'ouvre. 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 --> <!-- file: docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md -->
<!-- version: 2 --> <!-- version: 4 -->
# Séquence des releases fonctionnelles KSP # Séquence des releases fonctionnelles KSP
@@ -48,27 +48,24 @@ Stabiliser `ksp-core-lib` comme fondation N1 minimale et durable.
Le Core possède uniquement les contrats réellement transversaux nécessaires aux couches supérieures. Le Core possède uniquement les contrats réellement transversaux nécessaires aux couches supérieures.
### Périmètre initial ### Surface stabilisée
À auditer précisément dans `0.1.1-pre.001`, avec comme candidats acquis : `0.1.1` stabilise :
- type public commun `ksp_core_lib::Error` ; - `ksp_core_lib::ErrorCode`, `ErrorContext`, `Error` et `Result<T>` comme contrat d'erreur ouvert aux domaines supérieurs ;
- alias public commun `Result<T>` ou forme équivalente validée ; - `ksp_core_lib::Pubkey` comme primitive Solana réexportée par Core ;
- architecture d'erreur permettant aux domaines supérieurs d'ajouter du contexte sans faire connaître tous les futurs domaines à Core ; - 18 Program IDs fondamentaux possédés par KSP avec paires `PRGID_*` / `PRGIDPK_*` ;
- Program IDs fondamentaux appartenant à KSP Core ; - `declare_program_id!` pour construire la représentation texte et `Pubkey` depuis une déclaration canonique unique ;
- primitives/identités réellement communes et déjà justifiées ; - `ProgramIdEntry`, `ProgramIdFilter`, `ProgramIdKind` et le registre enumerable/recherchable ;
- conventions de version/provenance N1 uniquement si un besoin concret existe ; - des vues par domaine/famille/protocole et `native_program_ids()` sans registres secondaires ;
- exports crate-root et documentation publique ; - une taxonomie extensible séparant notamment `subfamily` et `program_version` ;
- tests unitaires/integration appropriés ; - les réexports crate-root, rustdocs et tests publics correspondants.
- respect complet des règles Rust/workspace.
### Dépendances ### Dépendances
Core ne dépend pas de `ksp-logging-lib`, Config, Wallet, Store, Transport, Program ou Materializer. Core ne dépend pas de `ksp-logging-lib`, Config, Wallet, Store, Transport, Program ou Materializer.
Une primitive Solana/Anza officiellement stable peut être ajoutée seulement lorsqu'un item Core concret en a besoin. La seule dépendance externe directe de `ksp-core-lib` à la clôture est `solana-pubkey`, déclarée au workspace avec la génération `^4.3`, `default-features = false`, puis héritée par la crate avec `.workspace = true`. Aucune feature optionnelle supplémentaire n'est activée dans `0.1.1`.
`solana-pubkey` est un candidat naturel pour les Program IDs ; les autres primitives autorisées ne sont pas ajoutées par anticipation.
### Hors scope ### Hors scope
@@ -87,20 +84,20 @@ Une primitive Solana/Anza officiellement stable peut être ajoutée seulement lo
### Lifecycle de la release ### Lifecycle de la release
Le nombre de prereleases n'est pas figé avant `pre.001`. Trajectoire réellement suivie :
Trajectoire candidate :
```text ```text
pre.001 brainstorming + audit + plan détaillé pre.001 brainstorming + audit + plan détaillé
pre.002 Error/Result + fondation API pre.001-fix.001/.002 corrections de cadrage Program IDs/taxonomie
pre.003 primitives/Program IDs réellement retenus pre.002 Error/Result + fondation API
pre.004 compléments/tests/audits pre.002-fix.001 corrections de tests/lints
pre.005 validation finale/docs/cleanup/prompt 0.1.2 pre.003 Pubkey + Program IDs
pre.003-fix.001 politique Cargo workspace + corrections Clippy
pre.004 intégration Core + audits
pre.005 validation finale/docs/cleanup/prompt 0.1.2
rel.001 publication stable validée de 0.1.1
``` ```
Cette séquence est indicative. `pre.001` peut la modifier.
## `0.1.2` — Logging foundation ## `0.1.2` — Logging foundation
### Dépendances ### Dépendances
@@ -325,7 +322,7 @@ Les directions restent celles du roadmap :
Ces séries sont des objectifs fonctionnels, pas un calendrier contractuel. Ces séries sont des objectifs fonctionnels, pas un calendrier contractuel.
# Sélection de la première release # Progression de la série `0.1.x`
La première release fonctionnelle est : La première release fonctionnelle est :
@@ -333,10 +330,15 @@ La première release fonctionnelle est :
0.1.1 — Core foundation 0.1.1 — Core foundation
``` ```
Le prompt de démarrage associé est : Son prompt historique d'ouverture reste :
```text ```text
prompts/001-V0_1_1_START_PROMPT.md prompts/001-V0_1_1_START_PROMPT.md
``` ```
`0.0.3` est désormais la base fondatrice stable. La prochaine phase ouvre `0.1.1-pre.001` à partir du prompt final `prompts/001-V0_1_1_START_PROMPT.md`. `0.1.1-rel.001` publie la surface Core stable après validation complète de `pre.005`. Le commit de release reçoit le tag `v0.1.1`. La release suivante s'ouvre avec :
```text
0.1.2 — Logging foundation
prompts/002-V0_1_2_START_PROMPT.md
```

View File

@@ -0,0 +1,699 @@
<!-- file: docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md -->
<!-- version: 10 -->
# Plan KSP 0.1.1 — Core foundation
## Statut
Plan historique clôturé de `0.1.1`, établi par `0.1.1-pre.001`, consolidé jusqu'à `0.1.1-pre.005` puis fermé par `0.1.1-rel.001`.
La surface fonctionnelle Core prévue par ce plan est implémentée et validée. Le delta `rel.001` publie `workspace.package.version = "0.1.1"`; son commit doit recevoir le tag stable `v0.1.1` avant ouverture de `0.1.2`.
## Base auditée
Base fournie :
```text
0.0.3 stable
```
Constats sur l'archive reçue :
- `workspace.package.version = "0.0.3"` ;
- seul `crates/ksp-core-lib` est membre du workspace ;
- `ksp-core-lib` ne possède encore aucune dépendance ;
- `crates/ksp-core-lib/src/lib.rs` contient uniquement la façade/squelette minimal et les lints de crate ;
- le delta final `deltas/0.0.3/rel.001.md` indique que les validations Cargo de publication ont été exécutées avec succès par le user avant stabilisation ;
- le prompt `prompts/001-V0_1_1_START_PROMPT.md` présent dans l'archive correspond au prompt final attendu pour cette session.
Dans le workflow KSP, l'archive `khadhroony-solana-project-v0.0.3.zip` est produite directement par Gitea depuis le tag correspondant. Cette provenance suffit à considérer la base fournie comme la release stable/taguée `v0.0.3` attendue ; l'absence normale de `.git` dans l'archive n'ajoute pas de vérification Git supplémentaire à cette session.
## Mission bornée
`0.1.1` doit rendre `ksp-core-lib` immédiatement consommable par les prochaines fondations N1 sans lui faire absorber leurs responsabilités.
La surface retenue pour cette release est limitée à :
1. un contrat commun `Error` / `Result` ouvert aux domaines supérieurs ;
2. la primitive Solana d'adresse publique nécessaire aux Program IDs ;
3. les Program IDs fondamentaux du runtime Solana, leurs deux représentations canonique texte/`Pubkey` et un petit registre descriptif enumerable ;
4. les réexports crate-root, rustdocs et tests nécessaires à ces contrats.
Aucune autre primitive n'est ajoutée sans usage concret découvert pendant la release.
## Inventaire des contrats Core nécessaires maintenant
### `Error` / `Result`
Besoin concret immédiat : `0.1.2` Logging puis les autres crates KSP doivent pouvoir retourner une erreur KSP commune sans obliger leurs consommateurs à adopter une erreur propre à chaque couche.
Le modèle historique de bot3 fondé sur une enum centrale comportant des variantes telles que Config, Tracing, Tauri, HTTP ou DB n'est pas repris : une telle enum fait connaître à Core les domaines supérieurs et doit être modifiée à chaque nouveau domaine.
### `Pubkey`
Les Program IDs ont besoin d'une représentation Solana typée et commune. Core doit posséder cette dépendance fondamentale et peut réexporter le type afin que les crates KSP supérieures n'aient pas à importer directement la crate Solana correspondante uniquement pour manipuler un Program ID.
### Program IDs fondamentaux
Core doit posséder les identifiants fondamentaux du runtime Solana qui ne relèvent d'aucun protocole supérieur.
Cette propriété inclut les valeurs canoniques KSP, leur représentation `Pubkey`, leur nomenclature et un registre descriptif enumerable permettant de les inventorier/rechercher. Ce registre de constantes n'est pas le registry de dispatch de `ksp-program-lib` : il ne sélectionne aucun decoder, executor ou implémentation de programme et ne crée aucune enum fermée des protocoles.
## Contrat d'erreur retenu
### Forme générale
`pre.002` stabilise un type structuré et extensible plutôt qu'une enum fermée.
Surface conceptuelle :
```text
ErrorCode
domain: &'static str
code: &'static str
ErrorContext
key: &'static str
value: String
Error
code: ErrorCode
message: String
context: Vec<ErrorContext>
source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>
Result<T> = std::result::Result<T, Error>
```
La surface Rust de `pre.002` retient les constructeurs/getters explicites ainsi que des enrichissements consommant `self` : `Error::with_context(...)` et `Error::with_source(...)`. `ErrorCode::new(...)` est `const` afin que les crates supérieures puissent déclarer leurs codes sous forme de constantes. Les invariants suivants font partie du contrat.
### Invariants
- `Error` est le seul type d'erreur KSP commun exposé par Core.
- `Result<T>` est un alias public vers `std::result::Result<T, Error>`.
- `ErrorCode` sépare explicitement un domaine stable et un code stable sans enum centrale des domaines.
- Les domaines supérieurs définissent leurs propres constantes `ErrorCode` ; Core ne connaît pas Config, Logging, Wallet, Transport, Store, Tauri ou les protocoles.
- Les identifiants de domaine/code sont des chaînes statiques afin de favoriser des codes définis au code source et stables, pas des catégories dynamiques construites au runtime.
- `message` reste un diagnostic lisible par un humain.
- `ErrorContext` permet d'ajouter du contexte structuré sans étendre le type `Error` à chaque besoin métier.
- Les clés de contexte sont statiques ; les valeurs sont possédées.
- Le contexte d'erreur ne doit pas contenir de secret. Logging décidera plus tard quels champs sont effectivement émis.
- Une cause externe peut être conservée par `source` lorsqu'elle implémente `std::error::Error + Send + Sync + 'static`.
- Une erreur externe qui ne respecte pas ces bornes peut toujours être transformée explicitement en message/contexte sans être conservée comme `source`.
- `Error` implémente `std::fmt::Display` et `std::error::Error`.
- `Display` utilise exactement la forme `<domain>.<code>: <message>` ; il ne concatène pas automatiquement le contexte ou la chaîne de causes.
- Aucun `From<ExternalError>` générique ou inventaire de conversions propres aux futurs domaines n'est ajouté dans Core. Les crates propriétaires enveloppent explicitement leur cause avec leur propre `ErrorCode`.
- Aucun besoin de `Clone`, `Eq` ou `PartialEq` n'est imposé à `Error` : préserver une vraie cause d'erreur est prioritaire sur ces dérivations.
Exemple conceptuel de code qualifié :
```text
config.profile_missing
logging.initialization_failed
transport.http_request_failed
```
Ces exemples illustrent le contrat ouvert ; ils ne créent aucune connaissance de ces domaines dans Core.
## API publique Core prévue
La façade crate-root doit exposer directement les contrats consommables :
```text
ksp_core_lib::Error
ksp_core_lib::ErrorCode
ksp_core_lib::ErrorContext
ksp_core_lib::Result
ksp_core_lib::Pubkey
ksp_core_lib::ProgramIdEntry
ksp_core_lib::ProgramIdFilter
ksp_core_lib::ProgramIdKind
ksp_core_lib::entries
ksp_core_lib::program_ids
ksp_core_lib::program_ids_by_domain
ksp_core_lib::program_ids_by_family
ksp_core_lib::program_ids_by_protocol
ksp_core_lib::native_program_ids
ksp_core_lib::find_program_id
ksp_core_lib::find_program_pubkey
ksp_core_lib::declare_program_id!
ksp_core_lib::PRGID_*
ksp_core_lib::PRGIDPK_*
```
Les modules d'implémentation restent privés conformément aux règles Rust du dépôt.
Arborescence candidate après développement :
```text
crates/ksp-core-lib/
├── Cargo.toml
├── src/
│ ├── lib.rs
│ ├── error.rs
│ └── program_ids.rs
├── unit_tests/
│ ├── error.rs
│ └── program_ids.rs
└── tests/
└── public_api.rs
```
Cette arborescence peut être ajustée si l'implémentation révèle une séparation plus simple, sans introduire `mod.rs` ni `pub mod`.
## Audit Solana/Anza actuel
Audit effectué le 2026-08-14 sur les sources officielles actuelles du dépôt `anza-xyz/solana-sdk` et les métadonnées de publication correspondantes.
### `solana-pubkey`
État observé :
```text
solana-pubkey 4.3.0
MSRV officiel du workspace Solana SDK : Rust 1.89.0
```
La génération actuelle de `solana-pubkey` est une façade de compatibilité officielle sur `solana-address` : elle réexporte notamment `solana_address::Address` sous le nom `Pubkey`.
Décision pour `0.1.1` :
- conserver le vocabulaire/API KSP `Pubkey` déjà prévu par l'architecture ;
- utiliser directement `solana-pubkey` comme dépendance propriétaire de `ksp-core-lib` ;
- déclarer la génération retenue sous `[workspace.dependencies]` avec la contrainte explicite `^4.3`, la résolution auditée actuelle étant `4.3.0` ;
- définir `default-features = false` au niveau workspace puis consommer la dépendance dans `ksp-core-lib` avec `solana-pubkey.workspace = true` ;
- n'activer ni Borsh, ni Wincode, ni Serde, ni Rand, ni feature cryptographique par anticipation ;
- réexporter le type `Pubkey` depuis `ksp_core_lib` afin que son utilisation fasse partie intentionnellement du contrat Core.
L'audit final `pre.005` confirme qu'aucune feature optionnelle de `solana-pubkey` ne doit être activée dans `0.1.1`. La surface Core actuelle utilise uniquement l'identité `Pubkey`, la construction compile-time des Program IDs et les opérations déjà disponibles avec la dépendance retenue. Les features `alloc`, `borsh`, `bytemuck`, `curve25519`, `rand`, `serde`, `sha2`, `std` et `wincode` restent need-driven et seront activées uniquement dans la crate propriétaire lorsqu'un contrat réel l'exigera.
Le `cargo tree -p ksp-core-lib -e features` exécuté par le user montre des features de `solana-address` telles que `copy`, `decode`, `error`, `sanitize`, `syscalls` et `default`. Elles proviennent de la composition interne résolue de `solana-pubkey`/`solana-address` et ne justifient pas d'activer une feature optionnelle KSP supplémentaire sur `solana-pubkey`.
Depuis `pre.003-fix.001`, la règle générale KSP impose la centralisation des dépendances externes sous `[workspace.dependencies]` et l'héritage `.workspace = true` dans les crates membres. La résolution Cargo observée pour la contrainte `^4.3` reste `solana-pubkey 4.3.0` au moment de cette tranche.
### `solana-address`
`solana-address 2.7.0` est la primitive interne actuelle derrière `solana-pubkey`.
Elle n'est pas ajoutée directement à KSP en `0.1.1` : l'ajouter en parallèle n'apporte aucun besoin Core supplémentaire et multiplierait les points d'entrée publics pour la même identité Solana.
### `solana-sdk-ids`
La source officielle `solana-sdk-ids` reste utile comme référence d'audit des identifiants publiés par Anza/Solana.
État du package observé pendant `pre.001` :
```text
solana-sdk-ids 3.1.0
```
Décision acquise : **ne pas ajouter `solana-sdk-ids` à KSP**, ni comme dépendance runtime, ni comme dev-dependency.
KSP possède ses propres constantes et registres de Program IDs. Les sources officielles Anza/Solana sont consultées pour vérifier les valeurs et l'évolution de la surface, mais cette vérification ne doit pas créer une dépendance Cargo à leur crate d'IDs.
### Crates explicitement non nécessaires à `0.1.1`
Ne pas ajouter :
- `solana-sdk` umbrella ;
- `solana-keypair` ;
- `solana-signer` ;
- `solana-hash` ;
- `solana-nonce` ;
- crates transaction/message/instruction ;
- crates RPC/client ;
- Borsh/Wincode/Serde pour une hypothétique future surface wire.
La génération retenue de `solana-pubkey` requiert Rust 1.89.0. La validation `pre.002-fix.001` a été exécutée avec une génération Clippy Rust 1.94.0, donc la toolchain observée satisfait ce MSRV. Une future révision ne devra pas revenir silencieusement à une vieille génération Solana uniquement pour contourner une exigence de toolchain.
## Program IDs retenus pour la première surface Core
`pre.003` fixe la première surface Core à **18 Program IDs**.
Les 17 identifiants fondamentaux exposés par la surface officielle actuelle `solana-sdk-ids` sont recopiés comme valeurs KSP sans créer de dépendance Cargo vers cette crate :
- System ;
- Stake ;
- Vote ;
- Config ;
- Feature ;
- Compute Budget ;
- Address Lookup Table ;
- BPF Loader historique v1 ;
- BPF Loader v2 ;
- BPF Loader Upgradeable ;
- Loader v4 ;
- Native Loader ;
- précompiles Ed25519, Secp256k1 et Secp256r1 ;
- ZK ElGamal Proof ;
- ZK Token Proof.
Le dix-huitième identifiant est le Slashing Program `S1ashing11111111111111111111111111111111111`. Il n'est pas encore publié dans `solana-sdk-ids`, mais son statut de programme enshrined et son adresse sont confirmés par SIMD-0204 et la documentation Anza. L'ancien `ks-program-ids` de bot3 avait déjà cette entrée ; `pre.003` ne la conserve toutefois qu'après cette revérification officielle indépendante.
### Exclusions volontaires de `0.1.1`
Ne pas ajouter fonctionnellement pendant cette release :
- SPL Token, Token-2022, ATA, Memo ou tout autre protocole SPL ;
- Metaplex et autres protocoles ;
- `incinerator`, qui est une adresse spéciale et non un Program ID exécutable ;
- les sysvar account IDs ;
- les autres well-known accounts non exécutables uniquement pour agrandir la première surface.
Les exemples SPL/protocoles utilisés pour définir la nomenclature ci-dessous illustrent la convention future et ne modifient pas le hors-scope de `0.1.1`.
Les well-known account IDs doivent rester explicitement séparés des Program IDs. L'ancien `native_well_known_account_ids()` constitue une bonne direction conceptuelle ; aucune API vide n'est toutefois créée en `0.1.1` tant qu'aucun well-known account n'est réellement retenu dans la surface de la release.
## Nomenclature des Program IDs
La nomenclature des symboles et la taxonomie du registre sont deux contrats liés mais distincts.
Le nom Rust d'une constante doit privilégier une **identité stable** et ne doit pas embarquer toute la classification fonctionnelle, car une reclassification future ne doit pas obliger à renommer un symbole public.
La forme générale devient :
```text
PRGID_<NAMESPACE>_<PROGRAM_OR_FAMILY>_<VARIANT?>_<VERSION?> -> &'static str Base58
PRGIDPK_<NAMESPACE>_<PROGRAM_OR_FAMILY>_<VARIANT?>_<VERSION?> -> Pubkey
```
Règles :
- `PRGID_` identifie toujours la représentation texte Base58 ;
- `PRGIDPK_` identifie toujours la représentation `Pubkey` ;
- le suffixe après le préfixe doit être identique entre les deux formes ;
- `NAMESPACE` identifie le namespace/propriétaire stable de l'identité, par exemple `SOLANA`, `SPL`, `METAPLEX`, `RAYDIUM`, `METEORA`, `GOOSEFX` ou `JUPITER` ;
- `PROGRAM_OR_FAMILY` et `VARIANT` décrivent l'identité publique utile du programme sans tenter de recopier mécaniquement tous les champs de `ProgramIdEntry` ;
- `VERSION` n'est ajoutée que lorsque le projet/protocole distingue réellement plusieurs générations d'une même lignée de programme ;
- un suffixe ressemblant à une année ou une version dans un nom officiel n'est pas automatiquement interprété comme `program_version` : `Token-2022` reste par exemple une identité de programme distincte et non une déduction automatique de version ;
- la valeur de version est un label KSP normalisé à partir de la génération publiquement reconnue (`V1`, `V2`, `V3`, `V4`, `V6`, `V0_5`, etc.), pas la version d'une crate ou d'une IDL.
Exemples de convention :
```text
PRGID_SOLANA_SYSTEM
PRGIDPK_SOLANA_SYSTEM
PRGID_SOLANA_LOADER_BPF_V2
PRGIDPK_SOLANA_LOADER_BPF_V2
PRGID_SOLANA_PRECOMPILE_ED25519
PRGIDPK_SOLANA_PRECOMPILE_ED25519
PRGID_SPL_MEMO_V1
PRGIDPK_SPL_MEMO_V1
PRGID_SPL_MEMO_V3
PRGIDPK_SPL_MEMO_V3
PRGID_SPL_MEMO_V4
PRGIDPK_SPL_MEMO_V4
PRGID_METEORA_DAMM_V2
PRGIDPK_METEORA_DAMM_V2
PRGID_GOOSEFX_GAMMA
PRGIDPK_GOOSEFX_GAMMA
PRGID_GOOSEFX_SSL_V2
PRGIDPK_GOOSEFX_SSL_V2
```
Les exemples SPL/DEX définissent uniquement la convention future pendant `0.1.1` ; ces protocoles restent hors scope fonctionnel de cette release.
### Pourquoi `NAMESPACE` et non `DOMAIN` dans le symbole
Le mot `domain` est réservé à la taxonomie fonctionnelle du registre décrite plus bas. Une constante doit rester stable si la classification fonctionnelle d'un programme est affinée.
Par exemple, `PRGID_GOOSEFX_GAMMA` reste un bon identifiant public même si KSP affine plus tard sa classification AMM. Le nom de symbole ne doit donc pas être une sérialisation complète de `domain/family/protocol/subfamily`.
## Construction et ownership des constantes
KSP possède la chaîne Base58 canonique de chaque Program ID et ne dépend pas d'un registre runtime externe pour la fournir.
La chaîne Base58 ne doit être écrite qu'une seule fois dans la déclaration KSP. Une macro publique KSP, nommée initialement `declare_program_id!`, doit produire les deux représentations à partir d'une déclaration unique, selon une forme conceptuelle de ce type :
```text
declare_program_id!(
PRGID_SOLANA_SYSTEM,
PRGIDPK_SOLANA_SYSTEM,
"11111111111111111111111111111111"
);
```
Le résultat conceptuel est :
```text
PRGID_SOLANA_SYSTEM : &'static str
PRGIDPK_SOLANA_SYSTEM : Pubkey
```
La macro doit s'inspirer de la mécanique compile-time de `solana_address::declare_id!`/des primitives correspondantes exposées via la génération `solana-pubkey`, mais KSP possède son API et sa nomenclature. L'implémentation exacte sera vérifiée en `pre.003` contre la version réellement retenue de `solana-pubkey`.
Invariants :
- aucune seconde copie manuelle de la valeur Base58 ;
- aucune conversion runtime inutile ;
- aucun `unwrap`, `expect`, `panic` ou opérateur `?` ;
- les deux constantes sont disponibles au crate-root ;
- la macro n'impose pas les symboles génériques `ID`, `id()` ou `check_id()` qui entreraient en collision lorsque plusieurs Program IDs sont déclarés dans Core.
Les sources officielles Solana/Anza servent :
1. de source de vérité externe pour vérifier la valeur Base58 ;
2. de contrôle de l'évolution des IDs/runtime ;
3. de référence d'audit ponctuelle sans dépendance Cargo.
## Registre descriptif des Program IDs
L'ancien `ks-program-ids` fournissait notamment :
```text
ProgramIdEntry
entries()
registered_program_ids()
native_program_ids()
native_well_known_account_ids()
find_registered_program_id()
```
Cette fonctionnalité doit être reprise et améliorée autour d'un **registre canonique unique**. Les vues spécialisées ne doivent pas maintenir des listes indépendantes et dupliquer les mêmes Program IDs.
### Axes de classification retenus
L'audit de l'ancien registre bot3 et des IDLs archivées montre qu'un seul axe hiérarchique ne suffit pas. La taxonomie KSP doit séparer au minimum :
- `domain` : domaine fonctionnel large ;
- `family` : famille fonctionnelle dans ce domaine ;
- `protocol` : protocole/projet auquel appartient le programme ;
- `subfamily` : branche, architecture ou produit interne optionnel dans une même famille/protocole ;
- `program_version` : génération publique optionnelle de la **lignée du programme on-chain** ;
- `kind` : classification technique nécessaire aux vues Core telles que les programmes natifs/loaders/précompiles ;
- le code KSP unique, la chaîne Base58 `PRGID_*` et le `Pubkey` `PRGIDPK_*` restent les identités de l'entrée.
Les vocabulaires de `domain`, `family`, `protocol`, `subfamily` et `program_version` restent des chaînes extensibles : Core ne crée aucune enum fermée des futurs protocoles Solana. `kind` est volontairement une petite enum technique `ProgramIdKind` (`Program`, `Loader`, `Precompile`, `EnshrinedProgram`) parce qu'elle décrit la nature de l'entrée plutôt qu'un catalogue de protocoles.
`family = amm` est retenu comme famille agrégatrice future pour les modèles AMM. Les variantes `cpmm`, `clmm`, `dlmm`, `damm`, `stable_swap`, `weighted_swap`, `gamma`, `ssl` ou équivalentes appartiennent au niveau `subfamily` lorsqu'elles représentent réellement une branche architecturale du protocole. Cela permettra à une future vue `amm_program_ids()` de retrouver l'ensemble de ces programmes au lieu de limiter la recherche à l'ancien préfixe bot3 `AMM_*`.
`subfamily` reste optionnelle : un programme unique peut supporter plusieurs courbes/mécanismes et ne doit pas être forcé artificiellement dans une seule sous-famille. L'AMM Aldrin constitue notamment un cas où le programme peut couvrir plusieurs types de courbes ; la famille `amm` suffit alors si aucune sous-famille unique n'est normative.
### `program_version` est distinct de `subfamily`
La version est un axe indépendant. Elle ne doit jamais être encodée comme une `subfamily` uniquement pour distinguer deux Program IDs.
Cas représentatifs audités :
| Cas | `domain`/`family`/`protocol` | `subfamily` | `program_version` |
|----------------------------|-------------------------------------------------------------------|-------------------|-------------------------------------------------------------|
| SPL Memo v1 / v3 / v4 | identiques entre les trois entrées | identique/absente | `v1` / `v3` / `v4` |
| Aldrin AMM v1 / v2 | identiques | identique/absente | `v1` / `v2` |
| Meteora DAMM v1 / v2 | identiques | `damm` | `v1` / `v2` |
| Meteora DLMM | même domaine/protocole AMM | `dlmm` | aucune si aucune génération normative n'est attachée à l'ID |
| GooseFX GAMMA | même domaine/famille/protocole GooseFX que les autres AMM GooseFX | `gamma` | aucune génération `v1` ne doit être inventée |
| GooseFX SSL v2 | même domaine/famille/protocole GooseFX | `ssl` | `v2` |
| Jupiter Aggregator v4 / v6 | identiques pour la lignée Aggregator | `aggregator` | `v4` / `v6` |
Cette séparation évite notamment de traiter GooseFX `GAMMA` comme « V1 » de GooseFX `SSL V2`, ce que les sources/IDLs ne justifient pas.
### Version du programme versus version d'IDL
`program_version` ne représente **jamais** la version de l'IDL, de la crate cliente, du SDK ou du schéma Anchor.
L'audit des IDLs archivées de bot3 démontre que ces nombres évoluent indépendamment du Program ID :
- l'IDL `jupiter_v6` porte une version d'IDL `0.1.0` alors que la génération publique du programme est `v6` ;
- l'IDL `goosefx_v2` porte une version d'IDL `0.3.0` alors que la génération publique est `v2` ;
- l'IDL GooseFX `gamma` porte une version de schéma `0.2.0` sans faire de GAMMA une hypothétique « v0.2 » du protocole ;
- l'IDL Meteora DAMM v2 peut porter une version de schéma différente de `v2`.
Une éventuelle provenance/version d'IDL appartient plus tard à la couche d'interface/decoder ou à ses métadonnées, pas à l'identité `ProgramIdEntry` de Core.
Le nom `protocol_version` n'est pas retenu : un même protocole peut posséder simultanément plusieurs composants/lignées versionnés indépendamment (par exemple un aggregator, un limit-order program, un vault ou un lending program). `program_version` borne correctement la version à l'entrée/lignée concernée.
### Recherches et vues
Direction de l'API :
```text
ProgramIdEntry
ProgramIdFilter
entries()
program_ids(filter)
native_program_ids()
find_program_id()
```
Le filtre générique doit pouvoir combiner les axes, au minimum :
```text
domain
family
protocol
subfamily
program_version
kind
```
Des helpers lisibles peuvent être exposés lorsque leur usage est réel :
```text
program_ids_by_domain(...)
program_ids_by_family(...)
program_ids_by_protocol(...)
native_program_ids()
```
Une future surface possédant des Program IDs AMM pourra ajouter :
```text
amm_program_ids()
```
Cette fonction devra être une vue de la classification canonique (`family = amm`) et non un second registre manuel. `0.1.1` ne crée pas un helper AMM vide puisque les Program IDs AMM restent hors scope de la release, mais son ajout futur ne doit nécessiter aucune refonte de `ProgramIdEntry`.
Les vues filtrées retournent des iterators paresseux sur le registre canonique et n'allouent pas de collection intermédiaire. `entries()` reste la vue exhaustive sous forme de slice statique. `native_program_ids()` et les helpers `program_ids_by_domain(...)`, `program_ids_by_family(...)` et `program_ids_by_protocol(...)` sont des vues de cette même source.
`registered_program_ids()` de bot3 reste considéré comme un alias redondant de `entries()` et n'est pas repris automatiquement. Les well-known accounts suivent un registre/naming distinct lorsqu'ils deviennent nécessaires.
### Invariants du registre
`ProgramIdEntry` doit permettre :
- recherche exacte par chaîne Base58 et, si utile, par `Pubkey` ;
- recherche par domaine ;
- recherche par famille ;
- recherche par protocole ;
- recherche par sous-famille lorsqu'elle existe ;
- recherche par génération de programme lorsqu'elle existe ;
- intersections de plusieurs critères sans créer un registre secondaire par combinaison ;
- production des vues spécialisées telles que `native_program_ids()` et, plus tard, `amm_program_ids()` ;
- unicité des codes et Program IDs canoniques.
La classification est descriptive. Elle ne porte aucun decoder, executor, IDL ou capability de dispatch et ne devient pas le registry fonctionnel de `ksp-program-lib`.
### Validation externe de la taxonomie pendant `pre.001-fix.002`
Le réaudit a confronté l'inventaire bot3 et ses IDLs à plusieurs sources externes actuelles :
- la source officielle Agave de chargement SPL distingue explicitement les Memo `1.0.0`, `3.0.0` et `4.0.0` avec trois Program IDs distincts ;
- l'interface SPL Memo actuelle expose séparément les modules `v1`, `v3` et `v4` ;
- Solana Explorer/Solscan exposent encore les Program IDs correspondants ;
- les sources GooseFX distinguent le programme GAMMA et la lignée SSL/V2 ;
- les sources Meteora distinguent DAMM v1, DAMM v2 et DLMM ;
- les sources Jupiter distinguent les générations de son Swap Aggregator, notamment v4 et v6.
Ces cas valident la séparation `family` / `subfamily` / `program_version` et invalident l'utilisation de `subfamily` comme simple conteneur de version.
## Primitives communes supplémentaires
Aucune primitive supplémentaire n'est démontrée nécessaire pendant `pre.001`.
Sont donc explicitement reportés :
- identité/version de module générique ;
- provenance/processor version ;
- `Hash` ;
- `Nonce` ;
- temps/slot ;
- identifiants de transport/provider ;
- types de transaction/instruction/account ;
- traits de module ou de composant.
L'ancien `ks-core::ModuleKind` / `ModuleName` / `ModuleVersion` de bot3 n'est pas migré par défaut. Une convention de provenance ne sera ajoutée que lorsqu'un consommateur réel l'exigera.
## Stratégie de tests
### `pre.002` — Error
Tests unitaires externes sous `unit_tests/` pour vérifier au minimum :
- conservation du domaine/code ;
- message ;
- ajout et ordre du contexte ;
- format `Display` ;
- absence du contexte/source dans le rendu si ce choix est conservé ;
- conservation de `source()` ;
- `Send + Sync` du type commun au moyen d'un test de compilation approprié.
Test d'intégration sous `tests/` pour vérifier que `Error`, `ErrorCode`, `ErrorContext` et `Result` sont réellement consommables depuis le crate-root.
### `pre.003` — Pubkey et Program IDs
Tests unitaires externes pour vérifier les invariants internes éventuels.
Tests d'intégration pour vérifier :
- `ksp_core_lib::Pubkey` consommable depuis la façade ;
- type exact des constantes `PRGID_*` et `PRGIDPK_*` ;
- égalité entre chaque chaîne Base58 possédée par KSP et sa représentation `Pubkey` compile-time ;
- unicité des codes, chaînes Base58 et `Pubkey` du registre ;
- cohérence de `entries()`, `program_ids(...)`, `native_program_ids()` et `find_program_id()` ;
- filtres par `domain`, `family`, `protocol`, `subfamily`, `program_version` et `kind`, y compris leurs intersections ;
- absence de duplication des entrées entre registre canonique et vues spécialisées ;
- séparation entre Program IDs et well-known accounts ;
- conformité des valeurs avec les sources officielles Anza/Solana consultées par l'audit, sans dépendance `solana-sdk-ids`.
L'ordre de `entries()` ne devient un contrat public que s'il est explicitement documenté comme tel ; sinon les tests doivent vérifier les invariants sans imposer arbitrairement un ordre.
## Validations prévues
Après chaque modification Rust applicable :
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
```
Audits complémentaires prévus lorsque la dépendance Solana existe :
```bash
cargo tree -p ksp-core-lib
cargo tree -p ksp-core-lib -d
cargo tree -p ksp-core-lib -e features
```
Objectifs :
- confirmer que Core ne tire aucune couche KSP supérieure ;
- confirmer l'absence de codecs wire ajoutés par KSP ;
- examiner toute duplication de génération fondamentale ;
- vérifier que seules les features Solana nécessaires sont actives.
Les scripts/audits propres au dépôt seront exécutés s'ils existent réellement au moment de chaque tranche. Aucun script de ce type n'est présent dans l'archive `0.0.3` auditée.
## Découpage des prereleases
### `0.1.1-pre.001` — audit + brainstorming + plan
Objectifs :
- inventorier Core ;
- fixer la direction Error/Result ;
- borner les Program IDs ;
- vérifier la génération Solana actuelle ;
- décider les dépendances réellement candidates ;
- fixer tests, API et hors-scope ;
- ouvrir le plan `003-V0_1_1_CORE_FOUNDATION_PLAN.md`.
Pas de développement fonctionnel Core.
### `0.1.1-pre.002` — Error/Result
Objectifs :
- implémenter `ErrorCode`, `ErrorContext`, `Error` et `Result<T>` ;
- mettre en place les modules privés et réexports crate-root associés ;
- ajouter les tests unitaires externes et tests d'intégration de cette surface ;
- valider l'absence de connaissance des domaines supérieurs.
Aucune dépendance Solana n'est nécessaire à cette tranche.
### `0.1.1-pre.003` — Pubkey + Program IDs
Objectifs :
- revérifier les versions Solana/Anza au jour de l'implémentation ;
- vérifier la toolchain observée par rapport au MSRV de la génération retenue ;
- déclarer `solana-pubkey = { version = "^4.3", default-features = false }` sous `[workspace.dependencies]` et la consommer uniquement dans le propriétaire `ksp-core-lib` avec `solana-pubkey.workspace = true` ;
- réexporter `Pubkey` ;
- implémenter `declare_program_id!` et la paire `PRGID_*` / `PRGIDPK_*` ;
- finaliser à 18 l'inventaire des Program IDs fondamentaux à partir des sources officielles actuelles ;
- implémenter `ProgramIdEntry`, `ProgramIdFilter`, `ProgramIdKind`, `entries()`, `program_ids(...)`, les vues domain/family/protocol, `native_program_ids()`, `find_program_id()` et `find_program_pubkey()` ;
- implémenter la taxonomie extensible `domain` / `family` / `protocol` / `subfamily` / `program_version` / `kind` sans enum centrale fermée des protocoles ;
- garantir que les futurs helpers spécialisés comme `amm_program_ids()` puissent être des vues du registre canonique sans duplication ;
- ajouter les tests de conformité, d'unicité, de filtrage et de façade publique ;
- confirmer l'absence totale de dépendance `solana-sdk-ids` ;
- auditer le graphe/features réels après validation Cargo par le user.
### `0.1.1-pre.004` — intégration Core + audits
Résultat de l'audit d'intégration :
- `Error` / `Result`, `Pubkey` et le registre Program IDs composent une façade Core cohérente sans dépendance vers une couche KSP supérieure ;
- aucune primitive N1 supplémentaire n'est démontrée nécessaire ;
- les modules d'implémentation restent privés et les contrats consommables sont réexportés explicitement au crate-root ;
- le test d'intégration public vérifie aussi qu'un consommateur peut déclarer un `const ErrorCode`, usage requis par les futures crates propriétaires de domaines ;
- la rustdoc crate-level est complétée pour expliciter la frontière Core.
Validations `pre.004` exécutées avec succès par le user le 2026-08-14 :
- `cargo fmt --all` ;
- `cargo check --workspace` ;
- `cargo test --workspace` : 14 tests unitaires et 3 tests d'intégration publics réussis ;
- `cargo clippy --workspace --all-targets` : succès sans warning communiqué ;
- `cargo tree -p ksp-core-lib` : dépendance directe unique `solana-pubkey 4.3.0`, résolvant `solana-address 2.7.0` ;
- `cargo tree -p ksp-core-lib -d` : aucun doublon ;
- `cargo tree -p ksp-core-lib -e features` : graphe des features inspecté, sans besoin d'activer une feature optionnelle `solana-pubkey` supplémentaire pour le contrat Core actuel.
La tranche ne change ni le contrat Error, ni la taxonomie, ni l'inventaire des 18 Program IDs.
### `0.1.1-pre.005` — clôture
Résultat de clôture validé :
- aucune nouvelle primitive Core et aucune nouvelle feature `solana-pubkey` ne sont ajoutées ;
- les documents de référence de `0.1.1` sont réalignés avec la surface effectivement livrée ;
- aucun `README.md`/`USAGE.md` spécifique à la crate n'est ajouté : la rustdoc crate-level et les tests publics couvrent suffisamment la petite surface actuelle ;
- aucun changelog général n'existe dans la base actuelle, donc aucun fichier de changelog artificiel n'est créé uniquement pour cette release ;
- le prompt final `prompts/002-V0_1_2_START_PROMPT.md` est créé pour ouvrir Logging après publication stable de `0.1.1` ;
- la publication stable est réalisée séparément par `0.1.1-rel.001`, conformément au workflow de versionnement.
### `0.1.1-rel.001` — publication stable
La publication stable :
- passe `workspace.package.version` de `0.1.1-pre.5` à `0.1.1` ;
- enregistre les validations finales de `pre.005` exécutées avec succès par le user le 2026-08-14 ;
- confirme 14 tests unitaires et 3 tests d'intégration publics réussis ;
- confirme Clippy sans warning communiqué ;
- confirme `solana-pubkey 4.3.0` comme unique dépendance externe directe de `ksp-core-lib` ;
- confirme l'absence de doublons dans `cargo tree -d` ;
- confirme que le graphe de features ne justifie aucune feature optionnelle `solana-pubkey` supplémentaire ;
- marque `0.1.1` comme réalisée dans le roadmap et conserve le présent plan comme historique clôturé ;
- prépare le commit de release puis le tag stable `v0.1.1`.
Aucun contrat public, Program ID, dépendance ou feature n'est modifié par `rel.001`.
Un `pre.NNN-fix.NNN` corrige la tranche correspondante sans réécrire son historique.
## Hors scope confirmé
`0.1.1` n'ouvre pas :
- `ksp-logging-lib` ;
- `ksp-config-lib` ;
- Tauri ;
- wallet/keypair/signer ;
- Borsh/Wincode et autres codecs wire ;
- `ksp-interface-lib` ;
- Program decoder/registry/`ProgramExecutionPreparer` ;
- execution policy/orchestration ;
- transport RPC/WS/Helius/Yellowstone ;
- Store/PostgreSQL ;
- materializers ;
- workers/jobs/pipelines ;
- scenarios ;
- trading/ML.
## Questions ouvertes non bloquantes
Aucune question ouverte ne bloque la publication de `0.1.1`. Les capacités volontairement différées restent soumises à la règle need-driven des releases futures plutôt qu'à des placeholders Core.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/rules/RULES_DEPENDENCIES.md --> <!-- file: docs/rules/RULES_DEPENDENCIES.md -->
<!-- version: 7 --> <!-- version: 8 -->
# Règles des dépendances KSP # Règles des dépendances KSP
@@ -23,6 +23,14 @@ Elles complètent les règles Rust générales et le graphe de `docs/architectur
- **DEP-KSP-004** — Aucun `ksp-data-api` global n'est introduit uniquement pour éviter des conversions explicites entre modèles appartenant à des responsabilités différentes. - **DEP-KSP-004** — Aucun `ksp-data-api` global n'est introduit uniquement pour éviter des conversions explicites entre modèles appartenant à des responsabilités différentes.
- **DEP-KSP-005** — Une dépendance autorisée par le graphe n'est ajoutée au manifeste que lorsqu'un usage réel la justifie. - **DEP-KSP-005** — Une dépendance autorisée par le graphe n'est ajoutée au manifeste que lorsqu'un usage réel la justifie.
## Déclaration Cargo et centralisation workspace
- **DEP-CARGO-001** — Toute dépendance externe utilisée par une crate membre du workspace est déclarée une seule fois dans le `Cargo.toml` racine sous `[workspace.dependencies]`.
- **DEP-CARGO-002** — Une crate membre consomme une dépendance centralisée avec `<dependency>.workspace = true` et ne redéclare pas localement sa version.
- **DEP-CARGO-003** — Les options communes de résolution telles que `default-features` et la contrainte de version sont définies au niveau `[workspace.dependencies]`. Une crate membre n'ajoute localement que des features réellement propres à son usage lorsqu'elles sont nécessaires et compatibles avec l'héritage Cargo.
- **DEP-CARGO-004** — Lorsqu'une génération majeure/mineure compatible est retenue, KSP exprime explicitement l'intention sous forme caret `^M.m` (par exemple `^4.3`) plutôt qu'avec une écriture patch telle que `4.3.0`. Même si Cargo interprète aussi par défaut cette dernière comme une contrainte compatible caret, KSP normalise la syntaxe pour rendre l'intention manifeste. Un pin exact `=M.m.p` ou un bornage différent requiert une justification explicite.
- **DEP-CARGO-005** — Le `Cargo.lock` résout la version patch concrète à l'intérieur de la contrainte du workspace ; cette résolution ne remplace pas la politique de version déclarée dans le manifeste racine.
## Codecs wire et cohérence des versions ## Codecs wire et cohérence des versions
- **DEP-WIRE-001** — Pour les surfaces wire officielles KSP, les dépendances directes vers `borsh`, `wincode` ou codecs équivalents appartiennent normalement à `ksp-interface-lib`. - **DEP-WIRE-001** — Pour les surfaces wire officielles KSP, les dépendances directes vers `borsh`, `wincode` ou codecs équivalents appartiennent normalement à `ksp-interface-lib`.

View File

@@ -1,5 +1,5 @@
<!-- file: prompts/000-README.md --> <!-- file: prompts/000-README.md -->
<!-- version: 3 --> <!-- version: 4 -->
# Prompts KSP # Prompts KSP
@@ -21,4 +21,5 @@ Le prompt générique `0.1.x` a été affiné pendant `0.0.3` puis remplacé par
## Documents ## Documents
- [`001-V0_1_1_START_PROMPT.md`](001-V0_1_1_START_PROMPT.md) — prompt final ouvrant la première release fonctionnelle `0.1.1` après publication stable de `0.0.3`. - [`001-V0_1_1_START_PROMPT.md`](001-V0_1_1_START_PROMPT.md) — prompt historique ouvrant la première release fonctionnelle `0.1.1` après publication stable de `0.0.3` ;
- [`002-V0_1_2_START_PROMPT.md`](002-V0_1_2_START_PROMPT.md) — prompt final destiné à ouvrir `0.1.2 — Logging foundation` après publication stable de `0.1.1`.

View File

@@ -0,0 +1,335 @@
<!-- file: prompts/002-V0_1_2_START_PROMPT.md -->
<!-- version: 1 -->
# Prompt de démarrage KSP 0.1.2
**Statut : Final — à utiliser après validation et publication stable de `0.1.1`.**
## 1. Identité
Release fonctionnelle :
```text
0.1.2 — Logging foundation
```
Deuxième release fonctionnelle de Khadhroony Solana Project.
## 2. Mission
Introduire et stabiliser `ksp-logging-lib` comme façade KSP commune et propriétaire du logging/tracing runtime.
Cette crate doit être la seule crate KSP qui importe directement et configure la stack `tracing` nécessaire à la politique générale de logs. Les autres crates comportementales KSP doivent consommer la façade de `ksp-logging-lib` plutôt que définir chacune leur propre initialisation ou leur propre politique de tracing.
La release doit établir une surface assez générale pour Logging lui-même et pour les prochaines crates N1, sans ouvrir `ksp-config-lib`, Tauri, Transport, Wallet, Program, Store ou les autres couches supérieures.
## 3. Base requise
Base attendue :
```text
0.1.1 stable
```
La session commence uniquement après validation de la dernière prerelease de `0.1.1`, publication du delta final `rel.001` et tag stable :
```text
v0.1.1
```
Dans le workflow KSP, une archive Gitea nommée `khadhroony-solana-project-v0.1.1.zip` provient directement du tag correspondant et constitue une base stable suffisante pour la session.
## 4. État validé à préserver
`ksp-core-lib` fournit désormais la fondation N1 commune, notamment :
```text
ksp_core_lib::ErrorCode
ksp_core_lib::ErrorContext
ksp_core_lib::Error
ksp_core_lib::Result<T>
ksp_core_lib::Pubkey
```
ainsi que la propriété KSP des Program IDs fondamentaux et leur registre descriptif.
Logging peut dépendre de `ksp-core-lib` pour `Error` / `Result`. La relation inverse reste interdite : Core ne dépend pas de Logging.
La politique Cargo établie dans `0.1.1` doit être conservée :
- toute dépendance externe est déclarée au `Cargo.toml` racine sous `[workspace.dependencies]` ;
- une crate membre consomme ces dépendances avec `<crate>.workspace = true` ;
- les versions sont exprimées avec une génération compatible explicite telle que `^M.m`, sauf pin justifié ;
- les features et `default-features` sont activées uniquement lorsqu'un besoin concret le démontre.
## 5. Sources de vérité internes
Relire en priorité les fichiers réellement présents dans la base, notamment :
- `ROADMAP.md` ;
- `docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md` ;
- `docs/plans/003-V0_1_1_CORE_FOUNDATION_PLAN.md` ;
- `docs/architecture/002-LAYERS_AND_DEPENDENCIES.md` ;
- `docs/architecture/003-COMPONENT_CONTRACTS.md` ;
- `docs/architecture/004-COMPONENT_INVENTORY.md` ;
- `docs/architecture/005-DEPENDENCY_GRAPH.md` ;
- `docs/rules/RULES_DEPENDENCIES.md` ;
- `docs/rules/RULES_KSP.md` ;
- `docs/rules/RULES_RUST.md` ;
- `docs/rules/VERSION_WORKFLOW.md` ;
- `docs/IDEAS.md` ;
- les deltas `deltas/0.1.1/*`.
Relire aussi les règles/index racine supplémentaires présents au moment de la session. Ne jamais inventer un document absent de la base.
## 6. Sources externes normatives
Avant d'ajouter `tracing`, `tracing-subscriber`, `tracing-appender` ou toute crate liée :
- vérifier les versions publiées actuelles depuis les sources officielles Tokio/tracing et crates.io/docs.rs ;
- examiner leurs features et dépendances réelles ;
- identifier le MSRV/toolchain pertinent ;
- éviter d'activer les default features ou features optionnelles uniquement par commodité ;
- examiner `cargo tree` et `cargo tree -e features` après intégration.
La première prerelease doit choisir les dépendances réellement nécessaires à l'API retenue ; la présence architecturale d'une crate candidate n'oblige pas à l'ajouter si la surface finale n'en a pas besoin.
## 7. Première prerelease obligatoire : `0.1.2-pre.001`
`pre.001` est d'abord une prerelease de **brainstorming, audit et planification**.
Elle doit au minimum :
1. inventorier l'état réel du workspace et confirmer la création/absence actuelle de `ksp-logging-lib` ;
2. auditer la stack `tracing` officielle actuelle, ses versions, features et dépendances ;
3. définir précisément la frontière entre façade KSP, initialisation et backend/subscriber ;
4. déterminer si l'API publique principale doit utiliser des macros, des fonctions ou une combinaison ;
5. préserver les callsites/source locations réels pour les événements de logging ;
6. définir les niveaux KSP `error`, `warn`, `info`, `debug`, `trace` ;
7. définir les champs structurés communs utiles maintenant, notamment `target`, `domain`, `component` ou équivalents sans inventer une taxonomie trop rigide ;
8. définir le contrat de settings runtime de Logging sans dépendre de Config ;
9. définir le lifecycle d'initialisation, les erreurs d'initialisation et le comportement d'une initialisation répétée ;
10. étudier console, fichiers, filtering, appender non bloquant et rotation uniquement selon les besoins de la première surface ;
11. traiter explicitement la durée de vie/ownership des guards nécessaires aux writers non bloquants si cette voie est retenue ;
12. définir la stratégie de prévention des secrets dans les logs ;
13. proposer l'API publique et les crate-root reexports ;
14. proposer les tests unitaires/intégration et audits de dépendances ;
15. dimensionner les prereleases suivantes ;
16. confirmer les hors-scope.
Ne pas transformer `pre.001` en une grosse phase de développement avant validation du plan.
## 8. Responsabilité de `ksp-logging-lib`
La direction acquise est :
```text
ksp-logging-lib
-> ksp-core-lib
-> tracing stack réellement retenue
```
`ksp-logging-lib` doit être la façade commune de logging/tracing du projet et le propriétaire de la politique runtime correspondante.
Les crates comportementales KSP pourront dépendre directement de `ksp-logging-lib` et utiliser sa surface KSP pour :
```text
error
warn
info
debug
trace
```
avec les champs structurés réellement retenus par `pre.001`.
Les crates `*-api` purement déclaratives restent sans dépendance logging par défaut lorsqu'elles n'ont aucun comportement réel à tracer.
## 9. Callsite et macros
L'audit doit porter une attention particulière à la préservation du callsite réel.
Une simple fonction wrapper autour d'une macro `tracing::*` peut enregistrer le fichier/module/ligne du wrapper plutôt que ceux de l'appelant. La surface KSP doit donc être conçue pour préserver correctement les métadonnées de callsite, quitte à exposer des macros KSP qui délèguent aux macros `tracing` au point d'appel.
Le design exact des macros/fonctions et leurs noms sont décidés dans `pre.001`, puis testés avant stabilisation.
## 10. Settings et Config
`ksp-logging-lib` ne dépend pas de `ksp-config-lib`.
Logging possède les settings runtime strictement nécessaires à son initialisation. `ksp-config-lib`, lorsqu'il sera développé en `0.1.3`, pourra lire/résoudre ses documents puis convertir explicitement la configuration obtenue vers les settings publics de Logging.
Ne pas introduire de document JSON/TOML de configuration global dans Logging uniquement pour anticiper Config.
## 11. Sorties et lifecycle
Le `pre.001` doit déterminer la première surface réellement utile parmi :
- console/stdout/stderr ;
- fichiers ;
- filtrage global et/ou par target/domain ;
- format lisible et/ou structuré ;
- rotation ;
- writer non bloquant ;
- flush/shutdown propre.
Lorsque `tracing-appender::non_blocking` ou un mécanisme équivalent est retenu, la durée de vie du guard doit être possédée par un objet/lifecycle KSP explicite afin d'éviter une perte silencieuse de logs à la fin du scope d'initialisation.
Une capacité non nécessaire à la première validation n'est pas ajoutée par anticipation.
## 12. Erreurs
Les erreurs Logging utilisent le contrat Core :
```text
ksp_core_lib::Error
ksp_core_lib::Result<T>
```
`ksp-logging-lib` définit ses propres constantes `ErrorCode` dans son domaine sans ajouter de variante ou de connaissance Logging à `ksp-core-lib`.
Les causes externes utiles sont conservées via le contrat `source` Core lorsqu'elles satisfont les bornes prévues.
Aucun `unwrap`, `expect`, `panic` production ou opérateur `?` n'est utilisé.
## 13. Secrets et données sensibles
Logging ne doit pas devenir un canal de fuite de secrets.
`pre.001` doit au minimum définir :
- quels champs sont interdits par politique ;
- comment les settings sensibles sont exclus ;
- quelles données doivent être explicitement redacted/omises par les appelants ;
- si des helpers de redaction génériques sont réellement nécessaires maintenant.
Ne pas logger automatiquement les contenus de clés privées, seeds, passwords, tokens d'API ou autres secrets.
## 14. Dépendances et propriété
Interdictions :
```text
ksp-core-lib -X-> ksp-logging-lib
ksp-logging-lib -X-> ksp-config-lib
ksp-logging-lib -X-> wallet/transport/program/store/workers/jobs/apps
```
`ksp-logging-lib` est la seule crate KSP qui doit normalement importer directement `tracing` et réaliser la configuration du subscriber/appender retenu.
Une application Tauri future peut exceptionnellement devoir intégrer une crate/plugin tracing imposée par son framework. Cette exception reste au niveau adaptateur/application et ne crée pas une seconde politique de logging parallèle à `ksp-logging-lib`.
## 15. Hors scope strict de `0.1.2`
- `ksp-config-lib` et documents/profils Config ;
- application Tauri ;
- wallet/keypair/signer ;
- RPC/WS/providers ;
- Program decoding/execution ;
- Store/PostgreSQL ;
- materializers ;
- workers/jobs/pipelines ;
- scenarios ;
- trading/ML ;
- OpenTelemetry ou export réseau de traces, sauf besoin concret explicitement revalidé et borné ;
- observability distribuée complète.
## 16. Règles Rust et Cargo
Préserver les règles workspace, notamment :
- Rust 2024 ;
- async-first pour les I/O futures lorsque pertinent, sans rendre artificiellement async les appels de logging synchrones ;
- `unsafe` interdit ;
- `unwrap` / `expect` interdits ;
- `panic` interdit en production ;
- opérateur `?` interdit ;
- returns explicites, y compris dans les closures lorsque Clippy l'exige ;
- `unreachable_pub = deny` ;
- `missing_docs = warn` ;
- imports de traits seulement lorsque nécessaire ;
- réexports crate-root explicites ;
- pas de `mod.rs` ;
- pas de `pub(super)` / `pub(in ...)` ;
- code/Rustdoc en anglais ;
- tests unitaires externes au `src` selon la convention du dépôt ;
- dépendances externes centralisées sous `[workspace.dependencies]`.
## 17. Git et deltas
À partir de `0.1.x`, chaque delta est commité :
```text
pre.NNN
pre.NNN-fix.NNN
rel.NNN
```
Une erreur est corrigée par le delta suivant ; l'historique n'est pas réécrit.
Seul le commit final validé comme stable reçoit :
```text
v0.1.2
```
## 18. Dimensionnement indicatif
Le nombre réel de prereleases est décidé dans `pre.001`.
Trajectoire candidate uniquement :
```text
pre.001 audit + brainstorming + plan
pre.002 crate/settings + façade levels/callsites
pre.003 initialisation + console/filtering
pre.004 fichiers/appender/rotation/lifecycle si retenus
pre.005 intégration/tests/audits
pre.006 validation finale/docs/cleanup/prompt 0.1.3
```
Scinder une tranche si son périmètre devient trop large. Supprimer/réorganiser une tranche si le `pre.001` démontre qu'une capacité candidate n'est pas nécessaire.
## 19. Validations attendues
Lorsque les commandes sont applicables :
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
cargo tree -p ksp-logging-lib
cargo tree -p ksp-logging-lib -d
cargo tree -p ksp-logging-lib -e features
```
Exécuter également les scripts/audits réellement présents dans le dépôt.
Aucune validation non exécutée ne doit être déclarée réussie.
## 20. Critères de sortie
`0.1.2` peut être publiée stable lorsque :
- la façade Logging et son ownership sont clairs ;
- les callsites sont préservés par tests ;
- les cinq niveaux KSP nécessaires sont utilisables ;
- l'initialisation choisie est déterministe et testée ;
- console/fichiers/filtering/lifecycle retenus sont validés ;
- les erreurs utilisent Core sans dépendance inverse ;
- aucune dépendance Config ou domaine supérieur n'a été introduite ;
- les secrets sont protégés par une politique documentée/testable ;
- le graphe de dépendances/features est audité ;
- les validations workspace sont propres ;
- la documentation finale et le prompt de la release suivante sont prêts.
## 21. Release suivante
La release suivante prévue est :
```text
0.1.3 — ksp-config-lib
```
Son périmètre concret reste soumis au `pre.001` correspondant et peut être scindé si Config s'avère trop large pour une seule release.