v0.3.16-pre.002
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 638
|
# version: 639
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-raw-transaction-ingest-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-raw-transaction-lib", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib", "crates/ksp-worker-api", "crates/ksp-worker-raw-transaction-ingest-lib"]
|
members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-raw-transaction-ingest-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-raw-transaction-lib", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib", "crates/ksp-worker-api", "crates/ksp-worker-raw-transaction-ingest-lib"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.3.16-pre.1"
|
version = "0.3.16-pre.2"
|
||||||
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"
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
// file: crates/ksp-store-api/src/lib.rs
|
// file: crates/ksp-store-api/src/lib.rs
|
||||||
// version: 7
|
// version: 8
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
//! Backend-agnostic persistence contracts for KSP Store implementations.
|
//! Backend-agnostic persistence contracts for KSP Store implementations.
|
||||||
//!
|
//!
|
||||||
//! `ksp-store-api` owns persistent models plus the contracts that operate on
|
//! `ksp-store-api` owns persistent models plus the contracts that operate on
|
||||||
@@ -15,11 +13,9 @@
|
|||||||
//! Models and capabilities deliberately have separate private module homes.
|
//! Models and capabilities deliberately have separate private module homes.
|
||||||
//! Backend implementations, SQL, migrations, Config, Transport and runtime
|
//! Backend implementations, SQL, migrations, Config, Transport and runtime
|
||||||
//! dispatch remain outside this crate.
|
//! dispatch remain outside this crate.
|
||||||
|
|
||||||
mod capability;
|
mod capability;
|
||||||
mod error;
|
mod error;
|
||||||
mod model;
|
mod model;
|
||||||
|
|
||||||
/// Boxed async operation returned by object-safe Store capability contracts.
|
/// Boxed async operation returned by object-safe Store capability contracts.
|
||||||
pub use self::capability::StoreApiFuture;
|
pub use self::capability::StoreApiFuture;
|
||||||
/// Read capability for random-access RAW account-observation inspection.
|
/// Read capability for random-access RAW account-observation inspection.
|
||||||
@@ -94,6 +90,8 @@ pub use self::model::raw_outcome::RawAcquisitionWriteOutcome;
|
|||||||
pub use self::model::raw_outcome::RawEntityWriteOutcome;
|
pub use self::model::raw_outcome::RawEntityWriteOutcome;
|
||||||
/// Outcome for one deterministic acquisition observation write.
|
/// Outcome for one deterministic acquisition observation write.
|
||||||
pub use self::model::raw_outcome::RawObservationWriteOutcome;
|
pub use self::model::raw_outcome::RawObservationWriteOutcome;
|
||||||
|
/// Durable Store outcome for one variant-aware RAW transaction acquisition.
|
||||||
|
pub use self::model::raw_outcome::RawTransactionVariantWriteOutcome;
|
||||||
/// Maximum opaque query cursor length admitted by the Store API.
|
/// Maximum opaque query cursor length admitted by the Store API.
|
||||||
pub use self::model::raw_pagination::MAX_RAW_PAGE_CURSOR_BYTES;
|
pub use self::model::raw_pagination::MAX_RAW_PAGE_CURSOR_BYTES;
|
||||||
/// Backend-independent list query for complete canonical RAW account states.
|
/// Backend-independent list query for complete canonical RAW account states.
|
||||||
@@ -154,10 +152,24 @@ pub use self::model::raw_retention::RawTransactionRetentionTransition;
|
|||||||
pub use self::model::raw_retention::RawTransactionTombstone;
|
pub use self::model::raw_retention::RawTransactionTombstone;
|
||||||
/// Canonical source-independent N1 RAW transaction persisted by Store backends.
|
/// Canonical source-independent N1 RAW transaction persisted by Store backends.
|
||||||
pub use self::model::raw_transaction::RawTransaction;
|
pub use self::model::raw_transaction::RawTransaction;
|
||||||
|
/// Durable lifecycle status of one RAW transaction conflict case.
|
||||||
|
pub use self::model::raw_transaction::RawTransactionConflictStatus;
|
||||||
/// Persistable acquisition observation linked to one canonical RAW transaction.
|
/// Persistable acquisition observation linked to one canonical RAW transaction.
|
||||||
pub use self::model::raw_transaction::RawTransactionObservation;
|
pub use self::model::raw_transaction::RawTransactionObservation;
|
||||||
/// Durable backend-independent identity of one canonical RAW transaction.
|
/// Durable backend-independent identity of one canonical RAW transaction.
|
||||||
pub use self::model::raw_transaction::RawTransactionReference;
|
pub use self::model::raw_transaction::RawTransactionReference;
|
||||||
|
/// Validated backend-neutral result of comparing canonical and incoming RAW transaction variants.
|
||||||
|
pub use self::model::raw_transaction::RawTransactionVariantComparison;
|
||||||
|
/// Stable non-zero Store identity of one persisted RAW transaction variant.
|
||||||
|
pub use self::model::raw_transaction::RawTransactionVariantId;
|
||||||
|
/// Origin of one persisted RAW transaction variant.
|
||||||
|
pub use self::model::raw_transaction::RawTransactionVariantOrigin;
|
||||||
|
/// Durable reference to one persisted RAW transaction variant.
|
||||||
|
pub use self::model::raw_transaction::RawTransactionVariantReference;
|
||||||
|
/// Backend-neutral quality relation between canonical and incoming RAW transaction variants.
|
||||||
|
pub use self::model::raw_transaction::RawTransactionVariantRelation;
|
||||||
|
/// Stable backend-neutral reason explaining one RAW transaction variant relation.
|
||||||
|
pub use self::model::raw_transaction::RawTransactionVariantRelationReason;
|
||||||
/// Common KSP error type used by Store-facing contracts.
|
/// Common KSP error type used by Store-facing contracts.
|
||||||
pub use ksp_core_lib::Error;
|
pub use ksp_core_lib::Error;
|
||||||
/// Stable structured code identifying a KSP error category and condition.
|
/// Stable structured code identifying a KSP error category and condition.
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// file: crates/ksp-store-api/src/model/raw_outcome.rs
|
// file: crates/ksp-store-api/src/model/raw_outcome.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
/// Outcome for one canonical RAW entity in an idempotent persistence operation.
|
/// Outcome for one canonical RAW entity in an idempotent persistence operation.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
@@ -14,7 +13,6 @@ pub enum RawEntityWriteOutcome {
|
|||||||
/// Normal persistence skipped a durable purged tombstone.
|
/// Normal persistence skipped a durable purged tombstone.
|
||||||
SkippedPurged,
|
SkippedPurged,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Outcome for one deterministic acquisition observation write.
|
/// Outcome for one deterministic acquisition observation write.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
@@ -26,14 +24,12 @@ pub enum RawObservationWriteOutcome {
|
|||||||
/// No observation was recorded because the associated RAW entity was intentionally skipped.
|
/// No observation was recorded because the associated RAW entity was intentionally skipped.
|
||||||
NotRecorded,
|
NotRecorded,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combined outcome of one atomic canonical RAW entity plus observation acquisition.
|
/// Combined outcome of one atomic canonical RAW entity plus observation acquisition.
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
pub struct RawAcquisitionWriteOutcome {
|
pub struct RawAcquisitionWriteOutcome {
|
||||||
entity: crate::RawEntityWriteOutcome,
|
entity: crate::RawEntityWriteOutcome,
|
||||||
observation: crate::RawObservationWriteOutcome,
|
observation: crate::RawObservationWriteOutcome,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RawAcquisitionWriteOutcome {
|
impl RawAcquisitionWriteOutcome {
|
||||||
/// Creates one backend-independent atomic acquisition outcome.
|
/// Creates one backend-independent atomic acquisition outcome.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -46,10 +42,33 @@ impl RawAcquisitionWriteOutcome {
|
|||||||
pub const fn entity(&self) -> crate::RawEntityWriteOutcome {
|
pub const fn entity(&self) -> crate::RawEntityWriteOutcome {
|
||||||
return self.entity;
|
return self.entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the acquisition observation write outcome.
|
/// Returns the acquisition observation write outcome.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn observation(&self) -> crate::RawObservationWriteOutcome {
|
pub const fn observation(&self) -> crate::RawObservationWriteOutcome {
|
||||||
return self.observation;
|
return self.observation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Durable Store outcome for one RAW transaction acquisition after variant-aware convergence.
|
||||||
|
///
|
||||||
|
/// Every variant represents a successful Store-domain result. In particular,
|
||||||
|
/// [`Self::QuarantinedConflict`] is durable success and must not be reinterpreted as a
|
||||||
|
/// transport failure or terminal persistence error by callers.
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub enum RawTransactionVariantWriteOutcome {
|
||||||
|
/// First durable variant became the initial canonical transaction.
|
||||||
|
InsertedCanonical,
|
||||||
|
/// Incoming content exactly matched an already durable variant/canonical representation.
|
||||||
|
ObservedExact,
|
||||||
|
/// Incoming content was durably observed while the current canonical variant remained strictly more complete.
|
||||||
|
ObservedCompatibleLessComplete,
|
||||||
|
/// Incoming content was durably persisted and atomically promoted because it was strictly more complete.
|
||||||
|
PromotedCompatibleMoreComplete,
|
||||||
|
/// Divergent or incomparable content was durably preserved without changing the current canonical variant.
|
||||||
|
QuarantinedConflict,
|
||||||
|
/// Explicit force-rehydration restored a previously purged logical transaction into variant-aware storage.
|
||||||
|
Rehydrated,
|
||||||
|
/// Normal acquisition respected an existing purged tombstone and performed no rehydration.
|
||||||
|
SkippedPurged,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-store-api/src/model/raw_transaction.rs
|
// file: crates/ksp-store-api/src/model/raw_transaction.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
/// Durable backend-independent identity of one canonical RAW transaction.
|
/// Durable backend-independent identity of one canonical RAW transaction.
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
@@ -7,7 +7,6 @@ pub struct RawTransactionReference {
|
|||||||
network: crate::RawNetworkId,
|
network: crate::RawNetworkId,
|
||||||
signature: crate::RawTransactionSignature,
|
signature: crate::RawTransactionSignature,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RawTransactionReference {
|
impl RawTransactionReference {
|
||||||
/// Creates one durable transaction identity from network and canonical Solana signature.
|
/// Creates one durable transaction identity from network and canonical Solana signature.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -20,7 +19,6 @@ impl RawTransactionReference {
|
|||||||
pub fn network(&self) -> &crate::RawNetworkId {
|
pub fn network(&self) -> &crate::RawNetworkId {
|
||||||
return &self.network;
|
return &self.network;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the canonical transaction signature.
|
/// Returns the canonical transaction signature.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn signature(&self) -> crate::RawTransactionSignature {
|
pub const fn signature(&self) -> crate::RawTransactionSignature {
|
||||||
@@ -28,6 +26,195 @@ impl RawTransactionReference {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stable non-zero Store identity of one persisted RAW transaction variant.
|
||||||
|
///
|
||||||
|
/// The identifier is a backend-neutral surrogate. It deliberately does not encode
|
||||||
|
/// `content_hash`, provider identity, canonicality or any physical database key layout.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub struct RawTransactionVariantId(u64);
|
||||||
|
impl RawTransactionVariantId {
|
||||||
|
/// Creates one non-zero stable variant identifier.
|
||||||
|
pub fn try_new(value: u64) -> crate::Result<Self> {
|
||||||
|
if value == 0 {
|
||||||
|
return std::result::Result::Err(raw_variant_model_error("variant_id"));
|
||||||
|
}
|
||||||
|
return std::result::Result::Ok(Self(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the opaque numeric surrogate unchanged.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn get(&self) -> u64 {
|
||||||
|
return self.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Durable reference to one persisted RAW transaction variant.
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub struct RawTransactionVariantReference {
|
||||||
|
transaction: crate::RawTransactionReference,
|
||||||
|
variant_id: crate::RawTransactionVariantId,
|
||||||
|
}
|
||||||
|
impl RawTransactionVariantReference {
|
||||||
|
/// Creates one durable variant reference from logical transaction identity and surrogate id.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(transaction: crate::RawTransactionReference, variant_id: crate::RawTransactionVariantId) -> Self {
|
||||||
|
return Self { transaction, variant_id };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the logical transaction identity owning this variant.
|
||||||
|
#[must_use]
|
||||||
|
pub fn transaction(&self) -> &crate::RawTransactionReference {
|
||||||
|
return &self.transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the stable surrogate variant identifier.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn variant_id(&self) -> crate::RawTransactionVariantId {
|
||||||
|
return self.variant_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Origin of one persisted RAW transaction variant.
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub enum RawTransactionVariantOrigin {
|
||||||
|
/// Exact canonical RAW representation received from one acquisition source.
|
||||||
|
Native,
|
||||||
|
/// Explicitly synthesized canonical RAW representation derived from preserved parent variants.
|
||||||
|
Synthetic,
|
||||||
|
}
|
||||||
|
impl RawTransactionVariantOrigin {
|
||||||
|
/// Returns the stable persistence code for this origin.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn code(&self) -> &'static str {
|
||||||
|
return match self {
|
||||||
|
Self::Native => "native",
|
||||||
|
Self::Synthetic => "synthetic",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Backend-neutral quality relation between the current canonical RAW transaction and one incoming variant.
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub enum RawTransactionVariantRelation {
|
||||||
|
/// Canonical transaction content is exactly equal after exact comparison.
|
||||||
|
Exact,
|
||||||
|
/// Incoming content is compatible but provably less complete than the current canonical variant.
|
||||||
|
CompatibleLessComplete,
|
||||||
|
/// Incoming content is compatible and provably more complete than the current canonical variant.
|
||||||
|
CompatibleMoreComplete,
|
||||||
|
/// A canonical transaction invariant is contradicted by the incoming variant.
|
||||||
|
Conflict,
|
||||||
|
/// No contradiction must be asserted, but no safe dominance relation is proved.
|
||||||
|
Incomparable,
|
||||||
|
}
|
||||||
|
impl RawTransactionVariantRelation {
|
||||||
|
/// Returns the stable persistence/diagnostic code for this relation.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn code(&self) -> &'static str {
|
||||||
|
return match self {
|
||||||
|
Self::Exact => "exact",
|
||||||
|
Self::CompatibleLessComplete => "compatible_less_complete",
|
||||||
|
Self::CompatibleMoreComplete => "compatible_more_complete",
|
||||||
|
Self::Conflict => "conflict",
|
||||||
|
Self::Incomparable => "incomparable",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stable backend-neutral reason explaining one RAW transaction variant relation.
|
||||||
|
///
|
||||||
|
/// These codes describe Store-domain comparison evidence only. They do not encode a
|
||||||
|
/// provider, route, backend implementation or retry classification.
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub enum RawTransactionVariantRelationReason {
|
||||||
|
/// Exact canonical transaction content equality was proved.
|
||||||
|
ExactCanonicalContent,
|
||||||
|
/// The incoming variant differs only by explicitly proved truncated `logMessages`.
|
||||||
|
IncomingLogMessagesTruncated,
|
||||||
|
/// The current canonical variant differs only by explicitly proved truncated `logMessages`.
|
||||||
|
CanonicalLogMessagesTruncated,
|
||||||
|
/// The transaction slot contradicts the current canonical transaction.
|
||||||
|
SlotMismatch,
|
||||||
|
/// The optional block timestamp contradicts the current canonical transaction.
|
||||||
|
BlockTimeMismatch,
|
||||||
|
/// Canonical RAW format identity/version differs and no safe equivalence contract applies.
|
||||||
|
PayloadFormatMismatch,
|
||||||
|
/// Canonical RAW payload content contains a proved contradiction.
|
||||||
|
CanonicalPayloadConflict,
|
||||||
|
/// Equal content digests were observed for canonical representations that are not byte-identical.
|
||||||
|
ContentHashCollision,
|
||||||
|
/// Canonical representations differ but the current comparator cannot prove equality, dominance or contradiction safely.
|
||||||
|
UnsupportedCanonicalDifference,
|
||||||
|
}
|
||||||
|
impl RawTransactionVariantRelationReason {
|
||||||
|
/// Returns the stable persistence/diagnostic code for this reason.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn code(&self) -> &'static str {
|
||||||
|
return match self {
|
||||||
|
Self::ExactCanonicalContent => "exact_canonical_content",
|
||||||
|
Self::IncomingLogMessagesTruncated => "incoming_log_messages_truncated",
|
||||||
|
Self::CanonicalLogMessagesTruncated => "canonical_log_messages_truncated",
|
||||||
|
Self::SlotMismatch => "slot_mismatch",
|
||||||
|
Self::BlockTimeMismatch => "block_time_mismatch",
|
||||||
|
Self::PayloadFormatMismatch => "payload_format_mismatch",
|
||||||
|
Self::CanonicalPayloadConflict => "canonical_payload_conflict",
|
||||||
|
Self::ContentHashCollision => "content_hash_collision",
|
||||||
|
Self::UnsupportedCanonicalDifference => "unsupported_canonical_difference",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validated backend-neutral result of comparing the current canonical transaction with one incoming variant.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub struct RawTransactionVariantComparison {
|
||||||
|
reason: crate::RawTransactionVariantRelationReason,
|
||||||
|
relation: crate::RawTransactionVariantRelation,
|
||||||
|
}
|
||||||
|
impl RawTransactionVariantComparison {
|
||||||
|
/// Creates one relation/reason pair and rejects semantically inconsistent combinations.
|
||||||
|
pub fn try_new(relation: crate::RawTransactionVariantRelation, reason: crate::RawTransactionVariantRelationReason) -> crate::Result<Self> {
|
||||||
|
if !valid_variant_relation_reason(relation, reason) {
|
||||||
|
return std::result::Result::Err(raw_variant_model_error("relation_reason"));
|
||||||
|
}
|
||||||
|
return std::result::Result::Ok(Self { reason, relation });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the stable reason code explaining the comparison.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn reason(&self) -> crate::RawTransactionVariantRelationReason {
|
||||||
|
return self.reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the backend-neutral quality relation.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn relation(&self) -> crate::RawTransactionVariantRelation {
|
||||||
|
return self.relation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Durable lifecycle status of one RAW transaction conflict case.
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub enum RawTransactionConflictStatus {
|
||||||
|
/// At least one preserved variant still requires explicit or future automatic reconciliation.
|
||||||
|
Open,
|
||||||
|
/// The conflict currently has an explicit durable resolution while history remains preserved.
|
||||||
|
Resolved,
|
||||||
|
}
|
||||||
|
impl RawTransactionConflictStatus {
|
||||||
|
/// Returns the stable persistence code for this status.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn code(&self) -> &'static str {
|
||||||
|
return match self {
|
||||||
|
Self::Open => "open",
|
||||||
|
Self::Resolved => "resolved",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Canonical source-independent N1 RAW transaction persisted by Store backends.
|
/// Canonical source-independent N1 RAW transaction persisted by Store backends.
|
||||||
///
|
///
|
||||||
/// The payload must contain the complete KSP canonical transaction representation required
|
/// The payload must contain the complete KSP canonical transaction representation required
|
||||||
@@ -41,14 +228,12 @@ pub struct RawTransaction {
|
|||||||
reference: crate::RawTransactionReference,
|
reference: crate::RawTransactionReference,
|
||||||
slot: u64,
|
slot: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RawTransaction {
|
impl RawTransaction {
|
||||||
/// Creates one complete canonical RAW transaction.
|
/// Creates one complete canonical RAW transaction.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(reference: crate::RawTransactionReference, slot: u64, block_time: std::option::Option<crate::RawTimestamp>, payload: crate::RawPayload) -> Self {
|
pub fn new(reference: crate::RawTransactionReference, slot: u64, block_time: std::option::Option<crate::RawTimestamp>, payload: crate::RawPayload) -> Self {
|
||||||
return Self { block_time, payload, reference, slot };
|
return Self { block_time, payload, reference, slot };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the optional canonical block timestamp.
|
/// Returns the optional canonical block timestamp.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn block_time(&self) -> std::option::Option<crate::RawTimestamp> {
|
pub const fn block_time(&self) -> std::option::Option<crate::RawTimestamp> {
|
||||||
@@ -60,7 +245,6 @@ impl RawTransaction {
|
|||||||
pub fn payload(&self) -> &crate::RawPayload {
|
pub fn payload(&self) -> &crate::RawPayload {
|
||||||
return &self.payload;
|
return &self.payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the durable backend-independent transaction identity.
|
/// Returns the durable backend-independent transaction identity.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn reference(&self) -> &crate::RawTransactionReference {
|
pub fn reference(&self) -> &crate::RawTransactionReference {
|
||||||
@@ -73,7 +257,6 @@ impl RawTransaction {
|
|||||||
return self.slot;
|
return self.slot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Persistable acquisition observation linked to one canonical RAW transaction.
|
/// Persistable acquisition observation linked to one canonical RAW transaction.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct RawTransactionObservation {
|
pub struct RawTransactionObservation {
|
||||||
@@ -81,14 +264,12 @@ pub struct RawTransactionObservation {
|
|||||||
provenance: crate::RawAcquisitionProvenance,
|
provenance: crate::RawAcquisitionProvenance,
|
||||||
transaction: crate::RawTransactionReference,
|
transaction: crate::RawTransactionReference,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RawTransactionObservation {
|
impl RawTransactionObservation {
|
||||||
/// Creates one successful observation of a complete canonical RAW transaction.
|
/// Creates one successful observation of a complete canonical RAW transaction.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(observation_key: crate::RawObservationKey, transaction: crate::RawTransactionReference, provenance: crate::RawAcquisitionProvenance) -> Self {
|
pub fn new(observation_key: crate::RawObservationKey, transaction: crate::RawTransactionReference, provenance: crate::RawAcquisitionProvenance) -> Self {
|
||||||
return Self { observation_key, provenance, transaction };
|
return Self { observation_key, provenance, transaction };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the deterministic producer-owned observation idempotence key.
|
/// Returns the deterministic producer-owned observation idempotence key.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn observation_key(&self) -> crate::RawObservationKey {
|
pub const fn observation_key(&self) -> crate::RawObservationKey {
|
||||||
@@ -100,7 +281,6 @@ impl RawTransactionObservation {
|
|||||||
pub fn provenance(&self) -> &crate::RawAcquisitionProvenance {
|
pub fn provenance(&self) -> &crate::RawAcquisitionProvenance {
|
||||||
return &self.provenance;
|
return &self.provenance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the durable transaction identity observed by this acquisition.
|
/// Returns the durable transaction identity observed by this acquisition.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn transaction(&self) -> &crate::RawTransactionReference {
|
pub fn transaction(&self) -> &crate::RawTransactionReference {
|
||||||
@@ -108,6 +288,30 @@ impl RawTransactionObservation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn raw_variant_model_error(field: &'static str) -> crate::Error {
|
||||||
|
return crate::Error::new(crate::ERROR_CODE_RAW_MODEL_INVALID, "invalid RAW transaction variant model").with_context("field", field);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn valid_variant_relation_reason(relation: crate::RawTransactionVariantRelation, reason: crate::RawTransactionVariantRelationReason) -> bool {
|
||||||
|
return matches!(
|
||||||
|
(relation, reason),
|
||||||
|
(crate::RawTransactionVariantRelation::Exact, crate::RawTransactionVariantRelationReason::ExactCanonicalContent)
|
||||||
|
| (crate::RawTransactionVariantRelation::CompatibleLessComplete, crate::RawTransactionVariantRelationReason::IncomingLogMessagesTruncated)
|
||||||
|
| (crate::RawTransactionVariantRelation::CompatibleMoreComplete, crate::RawTransactionVariantRelationReason::CanonicalLogMessagesTruncated)
|
||||||
|
| (
|
||||||
|
crate::RawTransactionVariantRelation::Conflict,
|
||||||
|
crate::RawTransactionVariantRelationReason::SlotMismatch
|
||||||
|
| crate::RawTransactionVariantRelationReason::BlockTimeMismatch
|
||||||
|
| crate::RawTransactionVariantRelationReason::CanonicalPayloadConflict
|
||||||
|
| crate::RawTransactionVariantRelationReason::ContentHashCollision
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
crate::RawTransactionVariantRelation::Incomparable,
|
||||||
|
crate::RawTransactionVariantRelationReason::PayloadFormatMismatch | crate::RawTransactionVariantRelationReason::UnsupportedCanonicalDifference
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "../../unit_tests/model/raw_transaction.rs"]
|
#[path = "../../unit_tests/model/raw_transaction.rs"]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// file: crates/ksp-store-api/tests/release_completeness.rs
|
// file: crates/ksp-store-api/tests/release_completeness.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! Release-level boundary and completeness canaries for the backend-neutral Store API RAW surface.
|
//! Release-level boundary and completeness canaries for the backend-neutral Store API RAW surface.
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn v0_3_8_pre_003_exact_crate_root_export_inventory_is_stable() {
|
fn v0_3_8_pre_003_exact_crate_root_export_inventory_is_stable() {
|
||||||
let crate_root = include_str!("../src/lib.rs");
|
let crate_root = include_str!("../src/lib.rs");
|
||||||
@@ -57,6 +56,7 @@ fn v0_3_8_pre_003_exact_crate_root_export_inventory_is_stable() {
|
|||||||
"pub use self::model::raw_outcome::RawAcquisitionWriteOutcome;",
|
"pub use self::model::raw_outcome::RawAcquisitionWriteOutcome;",
|
||||||
"pub use self::model::raw_outcome::RawEntityWriteOutcome;",
|
"pub use self::model::raw_outcome::RawEntityWriteOutcome;",
|
||||||
"pub use self::model::raw_outcome::RawObservationWriteOutcome;",
|
"pub use self::model::raw_outcome::RawObservationWriteOutcome;",
|
||||||
|
"pub use self::model::raw_outcome::RawTransactionVariantWriteOutcome;",
|
||||||
"pub use self::model::raw_pagination::MAX_RAW_PAGE_CURSOR_BYTES;",
|
"pub use self::model::raw_pagination::MAX_RAW_PAGE_CURSOR_BYTES;",
|
||||||
"pub use self::model::raw_pagination::RawAccountStateQuery;",
|
"pub use self::model::raw_pagination::RawAccountStateQuery;",
|
||||||
"pub use self::model::raw_pagination::RawPage;",
|
"pub use self::model::raw_pagination::RawPage;",
|
||||||
@@ -87,15 +87,21 @@ fn v0_3_8_pre_003_exact_crate_root_export_inventory_is_stable() {
|
|||||||
"pub use self::model::raw_retention::RawTransactionRetentionTransition;",
|
"pub use self::model::raw_retention::RawTransactionRetentionTransition;",
|
||||||
"pub use self::model::raw_retention::RawTransactionTombstone;",
|
"pub use self::model::raw_retention::RawTransactionTombstone;",
|
||||||
"pub use self::model::raw_transaction::RawTransaction;",
|
"pub use self::model::raw_transaction::RawTransaction;",
|
||||||
|
"pub use self::model::raw_transaction::RawTransactionConflictStatus;",
|
||||||
"pub use self::model::raw_transaction::RawTransactionObservation;",
|
"pub use self::model::raw_transaction::RawTransactionObservation;",
|
||||||
"pub use self::model::raw_transaction::RawTransactionReference;",
|
"pub use self::model::raw_transaction::RawTransactionReference;",
|
||||||
|
"pub use self::model::raw_transaction::RawTransactionVariantComparison;",
|
||||||
|
"pub use self::model::raw_transaction::RawTransactionVariantId;",
|
||||||
|
"pub use self::model::raw_transaction::RawTransactionVariantOrigin;",
|
||||||
|
"pub use self::model::raw_transaction::RawTransactionVariantReference;",
|
||||||
|
"pub use self::model::raw_transaction::RawTransactionVariantRelation;",
|
||||||
|
"pub use self::model::raw_transaction::RawTransactionVariantRelationReason;",
|
||||||
];
|
];
|
||||||
expected.sort_unstable();
|
expected.sort_unstable();
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
assert!(!crate_root.contains("pub mod "));
|
assert!(!crate_root.contains("pub mod "));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn v0_3_8_pre_003_exact_production_module_inventory_is_raw_only() {
|
fn v0_3_8_pre_003_exact_production_module_inventory_is_raw_only() {
|
||||||
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
|
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
|
||||||
@@ -125,7 +131,6 @@ fn v0_3_8_pre_003_exact_production_module_inventory_is_raw_only() {
|
|||||||
assert_eq!(capability_names, std::vec!["raw_account.rs", "raw_retention.rs", "raw_transaction.rs"]);
|
assert_eq!(capability_names, std::vec!["raw_account.rs", "raw_retention.rs", "raw_transaction.rs"]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pre_007_public_evolutive_enums_remain_non_exhaustive() {
|
fn pre_007_public_evolutive_enums_remain_non_exhaustive() {
|
||||||
let sources = [
|
let sources = [
|
||||||
@@ -133,6 +138,7 @@ fn pre_007_public_evolutive_enums_remain_non_exhaustive() {
|
|||||||
include_str!("../src/model/raw_pagination.rs"),
|
include_str!("../src/model/raw_pagination.rs"),
|
||||||
include_str!("../src/model/raw_primitives.rs"),
|
include_str!("../src/model/raw_primitives.rs"),
|
||||||
include_str!("../src/model/raw_retention.rs"),
|
include_str!("../src/model/raw_retention.rs"),
|
||||||
|
include_str!("../src/model/raw_transaction.rs"),
|
||||||
];
|
];
|
||||||
for enum_name in [
|
for enum_name in [
|
||||||
"RawAcquisitionOrigin",
|
"RawAcquisitionOrigin",
|
||||||
@@ -142,12 +148,16 @@ fn pre_007_public_evolutive_enums_remain_non_exhaustive() {
|
|||||||
"RawRetentionWriteOutcome",
|
"RawRetentionWriteOutcome",
|
||||||
"RawSortDirection",
|
"RawSortDirection",
|
||||||
"RawTransactionAcquisitionMode",
|
"RawTransactionAcquisitionMode",
|
||||||
|
"RawTransactionConflictStatus",
|
||||||
|
"RawTransactionVariantOrigin",
|
||||||
|
"RawTransactionVariantRelation",
|
||||||
|
"RawTransactionVariantRelationReason",
|
||||||
|
"RawTransactionVariantWriteOutcome",
|
||||||
] {
|
] {
|
||||||
assert_non_exhaustive(&sources, enum_name);
|
assert_non_exhaustive(&sources, enum_name);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pre_007_interface_store_ownership_and_negative_scope_remain_explicit() {
|
fn pre_007_interface_store_ownership_and_negative_scope_remain_explicit() {
|
||||||
let interface_root = include_str!("../../ksp-interface-lib/src/lib.rs");
|
let interface_root = include_str!("../../ksp-interface-lib/src/lib.rs");
|
||||||
@@ -189,7 +199,6 @@ fn pre_007_interface_store_ownership_and_negative_scope_remain_explicit() {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn v0_3_8_pre_003_capability_inventory_stays_fine_grained_without_runtime_facade() {
|
fn v0_3_8_pre_003_capability_inventory_stays_fine_grained_without_runtime_facade() {
|
||||||
let sources = [
|
let sources = [
|
||||||
@@ -227,7 +236,6 @@ fn v0_3_8_pre_003_capability_inventory_stays_fine_grained_without_runtime_facade
|
|||||||
assert_eq!(traits, expected);
|
assert_eq!(traits, expected);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_non_exhaustive(sources: &[&str], enum_name: &str) {
|
fn assert_non_exhaustive(sources: &[&str], enum_name: &str) {
|
||||||
let needle = "#[non_exhaustive]\n";
|
let needle = "#[non_exhaustive]\n";
|
||||||
let declaration = std::format!("pub enum {enum_name}");
|
let declaration = std::format!("pub enum {enum_name}");
|
||||||
@@ -243,7 +251,6 @@ fn assert_non_exhaustive(sources: &[&str], enum_name: &str) {
|
|||||||
assert!(found, "public evolutive enum not found: {enum_name}");
|
assert!(found, "public evolutive enum not found: {enum_name}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rust_file_names(directory: &std::path::Path) -> std::io::Result<std::vec::Vec<std::string::String>> {
|
fn rust_file_names(directory: &std::path::Path) -> std::io::Result<std::vec::Vec<std::string::String>> {
|
||||||
let entries = match std::fs::read_dir(directory) {
|
let entries = match std::fs::read_dir(directory) {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-store-api/unit_tests/model/raw_transaction.rs
|
// file: crates/ksp-store-api/unit_tests/model/raw_transaction.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
fn network() -> std::option::Option<crate::RawNetworkId> {
|
fn network() -> std::option::Option<crate::RawNetworkId> {
|
||||||
return match crate::RawNetworkId::new("mainnet".to_owned()) {
|
return match crate::RawNetworkId::new("mainnet".to_owned()) {
|
||||||
@@ -83,3 +83,60 @@ fn transaction_observation_is_separate_from_canonical_raw_payload() {
|
|||||||
assert_eq!(observation.provenance().provider().as_str(), "publicnode");
|
assert_eq!(observation.provenance().provider().as_str(), "publicnode");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_variant_identity_is_non_zero_and_keeps_transaction_scope() {
|
||||||
|
assert!(crate::RawTransactionVariantId::try_new(0).is_err());
|
||||||
|
let variant_id = match crate::RawTransactionVariantId::try_new(7) {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return,
|
||||||
|
};
|
||||||
|
let network = match network() {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return,
|
||||||
|
};
|
||||||
|
let transaction = crate::RawTransactionReference::new(network, crate::RawTransactionSignature::new([13_u8; 64]));
|
||||||
|
let variant = crate::RawTransactionVariantReference::new(transaction, variant_id);
|
||||||
|
assert_eq!(variant.variant_id().get(), 7);
|
||||||
|
assert_eq!(variant.transaction().network().as_str(), "mainnet");
|
||||||
|
assert_eq!(crate::RawTransactionVariantOrigin::Native.code(), "native");
|
||||||
|
assert_eq!(crate::RawTransactionVariantOrigin::Synthetic.code(), "synthetic");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_variant_relation_reason_pairs_are_validated() {
|
||||||
|
let exact = crate::RawTransactionVariantComparison::try_new(
|
||||||
|
crate::RawTransactionVariantRelation::Exact,
|
||||||
|
crate::RawTransactionVariantRelationReason::ExactCanonicalContent,
|
||||||
|
);
|
||||||
|
assert!(exact.is_ok());
|
||||||
|
let invalid = crate::RawTransactionVariantComparison::try_new(
|
||||||
|
crate::RawTransactionVariantRelation::Exact,
|
||||||
|
crate::RawTransactionVariantRelationReason::IncomingLogMessagesTruncated,
|
||||||
|
);
|
||||||
|
assert!(invalid.is_err());
|
||||||
|
let conflict = crate::RawTransactionVariantComparison::try_new(
|
||||||
|
crate::RawTransactionVariantRelation::Conflict,
|
||||||
|
crate::RawTransactionVariantRelationReason::ContentHashCollision,
|
||||||
|
);
|
||||||
|
assert!(conflict.is_ok());
|
||||||
|
let incomparable = crate::RawTransactionVariantComparison::try_new(
|
||||||
|
crate::RawTransactionVariantRelation::Incomparable,
|
||||||
|
crate::RawTransactionVariantRelationReason::PayloadFormatMismatch,
|
||||||
|
);
|
||||||
|
assert!(incomparable.is_ok());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_variant_codes_and_outcomes_are_stable_backend_neutral_contracts() {
|
||||||
|
assert_eq!(crate::RawTransactionVariantRelation::CompatibleLessComplete.code(), "compatible_less_complete");
|
||||||
|
assert_eq!(crate::RawTransactionVariantRelationReason::IncomingLogMessagesTruncated.code(), "incoming_log_messages_truncated");
|
||||||
|
assert_eq!(crate::RawTransactionVariantRelationReason::ContentHashCollision.code(), "content_hash_collision");
|
||||||
|
assert_eq!(crate::RawTransactionConflictStatus::Open.code(), "open");
|
||||||
|
assert_eq!(crate::RawTransactionConflictStatus::Resolved.code(), "resolved");
|
||||||
|
assert_ne!(crate::RawTransactionVariantWriteOutcome::InsertedCanonical, crate::RawTransactionVariantWriteOutcome::ObservedExact);
|
||||||
|
assert_ne!(crate::RawTransactionVariantWriteOutcome::QuarantinedConflict, crate::RawTransactionVariantWriteOutcome::PromotedCompatibleMoreComplete);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|||||||
262
deltas/0.3.16/pre.002.md
Normal file
262
deltas/0.3.16/pre.002.md
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
<!-- file: deltas/0.3.16/pre.002.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta `0.3.16-pre.002` — contrats Store API RAW multi-variantes
|
||||||
|
|
||||||
|
## Base requise
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.3.16-pre.001 appliquée
|
||||||
|
0.3.16-pre.001-fix.001 appliqué
|
||||||
|
workspace.package.version = 0.3.16-pre.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Le gate Markdown du fix précédent a été fourni propre le 20 septembre 2026 :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Markdown table audit: clean (352 table(s), 942 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Figer les contrats backend-neutral indispensables à la persistance RAW transaction multi-variantes avant d'introduire la migration PostgreSQL V003.
|
||||||
|
|
||||||
|
Cette tranche ajoute uniquement des modèles/types Store API. Elle ne modifie encore :
|
||||||
|
|
||||||
|
- aucune migration SQL ;
|
||||||
|
- aucun backend PostgreSQL ;
|
||||||
|
- aucun `RawTransactionWrite` existant ;
|
||||||
|
- aucun Worker ;
|
||||||
|
- aucun comparateur de contenu `logMessages` ;
|
||||||
|
- aucune UI Store Desk.
|
||||||
|
|
||||||
|
## Version workspace
|
||||||
|
|
||||||
|
`Cargo.toml` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
header version : 638 -> 639
|
||||||
|
workspace : 0.3.16-pre.1 -> 0.3.16-pre.2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fichiers modifiés
|
||||||
|
|
||||||
|
```text
|
||||||
|
Cargo.toml
|
||||||
|
crates/ksp-store-api/src/lib.rs
|
||||||
|
crates/ksp-store-api/src/model/raw_outcome.rs
|
||||||
|
crates/ksp-store-api/src/model/raw_transaction.rs
|
||||||
|
crates/ksp-store-api/tests/release_completeness.rs
|
||||||
|
crates/ksp-store-api/unit_tests/model/raw_transaction.rs
|
||||||
|
docs/plans/038-V0_3_16_RAW_RESILIENCE_CONFLICT_PLAN.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fichier ajouté
|
||||||
|
|
||||||
|
```text
|
||||||
|
deltas/0.3.16/pre.002.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contrats ajoutés
|
||||||
|
|
||||||
|
### Identité de variante
|
||||||
|
|
||||||
|
```text
|
||||||
|
RawTransactionVariantId
|
||||||
|
RawTransactionVariantReference
|
||||||
|
```
|
||||||
|
|
||||||
|
`RawTransactionVariantId` est un surrogate backend-neutral strictement non nul. Il ne dérive ni de `content_hash`, ni du provider, ni d'une clé physique SQL.
|
||||||
|
|
||||||
|
`RawTransactionVariantReference` conserve explicitement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
RawTransactionReference
|
||||||
|
+
|
||||||
|
RawTransactionVariantId
|
||||||
|
```
|
||||||
|
|
||||||
|
Le hash reste donc un attribut de contenu et jamais l'identité métier de la variante.
|
||||||
|
|
||||||
|
### Origine de variante
|
||||||
|
|
||||||
|
```text
|
||||||
|
RawTransactionVariantOrigin::Native
|
||||||
|
RawTransactionVariantOrigin::Synthetic
|
||||||
|
```
|
||||||
|
|
||||||
|
Les codes persistables stables sont respectivement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
native
|
||||||
|
synthetic
|
||||||
|
```
|
||||||
|
|
||||||
|
Une variante synthétique est seulement un contrat réservé ; aucune fusion synthétique n'est implémentée dans cette tranche.
|
||||||
|
|
||||||
|
### Relation de qualité
|
||||||
|
|
||||||
|
```text
|
||||||
|
RawTransactionVariantRelation::Exact
|
||||||
|
RawTransactionVariantRelation::CompatibleLessComplete
|
||||||
|
RawTransactionVariantRelation::CompatibleMoreComplete
|
||||||
|
RawTransactionVariantRelation::Conflict
|
||||||
|
RawTransactionVariantRelation::Incomparable
|
||||||
|
```
|
||||||
|
|
||||||
|
La direction du contrat est toujours :
|
||||||
|
|
||||||
|
```text
|
||||||
|
current canonical
|
||||||
|
vs
|
||||||
|
incoming variant
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucune heuristique provider-majority ou source authority n'est introduite.
|
||||||
|
|
||||||
|
### Reason codes
|
||||||
|
|
||||||
|
`RawTransactionVariantRelationReason` fige les raisons backend-neutral suivantes :
|
||||||
|
|
||||||
|
```text
|
||||||
|
ExactCanonicalContent
|
||||||
|
IncomingLogMessagesTruncated
|
||||||
|
CanonicalLogMessagesTruncated
|
||||||
|
SlotMismatch
|
||||||
|
BlockTimeMismatch
|
||||||
|
PayloadFormatMismatch
|
||||||
|
CanonicalPayloadConflict
|
||||||
|
ContentHashCollision
|
||||||
|
UnsupportedCanonicalDifference
|
||||||
|
```
|
||||||
|
|
||||||
|
Chaque raison possède un `code()` stable destiné à la persistance et aux diagnostics sûrs.
|
||||||
|
|
||||||
|
Les reason codes ne transportent ni URL, ni provider concret, ni secret, ni payload RAW.
|
||||||
|
|
||||||
|
### Couple relation/reason validé
|
||||||
|
|
||||||
|
```text
|
||||||
|
RawTransactionVariantComparison
|
||||||
|
```
|
||||||
|
|
||||||
|
`try_new(...)` refuse les couples incohérents.
|
||||||
|
|
||||||
|
Contrats actuellement admis :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Exact <-> ExactCanonicalContent
|
||||||
|
CompatibleLessComplete <-> IncomingLogMessagesTruncated
|
||||||
|
CompatibleMoreComplete <-> CanonicalLogMessagesTruncated
|
||||||
|
Conflict <-> SlotMismatch | BlockTimeMismatch | CanonicalPayloadConflict | ContentHashCollision
|
||||||
|
Incomparable <-> PayloadFormatMismatch | UnsupportedCanonicalDifference
|
||||||
|
```
|
||||||
|
|
||||||
|
Cette validation ne constitue pas encore le comparateur de contenu. Le comparateur pur est prévu en `0.3.16-pre.005`.
|
||||||
|
|
||||||
|
### Outcome de persistance variant-aware
|
||||||
|
|
||||||
|
```text
|
||||||
|
RawTransactionVariantWriteOutcome::InsertedCanonical
|
||||||
|
RawTransactionVariantWriteOutcome::ObservedExact
|
||||||
|
RawTransactionVariantWriteOutcome::ObservedCompatibleLessComplete
|
||||||
|
RawTransactionVariantWriteOutcome::PromotedCompatibleMoreComplete
|
||||||
|
RawTransactionVariantWriteOutcome::QuarantinedConflict
|
||||||
|
RawTransactionVariantWriteOutcome::Rehydrated
|
||||||
|
RawTransactionVariantWriteOutcome::SkippedPurged
|
||||||
|
```
|
||||||
|
|
||||||
|
`QuarantinedConflict` est explicitement un succès durable Store : une divergence conservée ne doit pas être transformée en faute Transport ou en erreur terminale Worker.
|
||||||
|
|
||||||
|
Aucune capability existante ne retourne encore ce nouvel outcome dans `pre.002`; son adoption backend intervient après V003.
|
||||||
|
|
||||||
|
### Statut minimal du conflict case
|
||||||
|
|
||||||
|
```text
|
||||||
|
RawTransactionConflictStatus::Open
|
||||||
|
RawTransactionConflictStatus::Resolved
|
||||||
|
```
|
||||||
|
|
||||||
|
Le détail des actions opérateur, revisions attendues, reopen et historique append-only reste réservé au programme `0.3.17`.
|
||||||
|
|
||||||
|
## Compatibilité
|
||||||
|
|
||||||
|
Les contrats historiques restent intacts :
|
||||||
|
|
||||||
|
```text
|
||||||
|
RawTransaction
|
||||||
|
RawTransactionObservation
|
||||||
|
RawTransactionReference
|
||||||
|
RawAcquisitionWriteOutcome
|
||||||
|
RawEntityWriteOutcome
|
||||||
|
RawObservationWriteOutcome
|
||||||
|
RawTransactionWrite
|
||||||
|
RawTransactionRead
|
||||||
|
```
|
||||||
|
|
||||||
|
`pre.002` n'impose donc aucune migration immédiate aux consumers existants.
|
||||||
|
|
||||||
|
Tous les nouveaux enums évolutifs sont `#[non_exhaustive]` et les exports restent disponibles uniquement depuis la racine `ksp-store-api` conformément aux règles KSP.
|
||||||
|
|
||||||
|
## Tests/canaris ajoutés ou étendus
|
||||||
|
|
||||||
|
Les tests couvrent notamment :
|
||||||
|
|
||||||
|
```text
|
||||||
|
variant_id = 0 rejeté
|
||||||
|
variant_id non nul conservé sans encoder content_hash
|
||||||
|
référence variante conserve le scope transaction
|
||||||
|
codes Native/Synthetic stables
|
||||||
|
couples relation/reason valides acceptés
|
||||||
|
couples incohérents rejetés
|
||||||
|
codes relation/reason/status stables
|
||||||
|
outcomes variant-aware distincts
|
||||||
|
inventaire exact des exports crate-root mis à jour
|
||||||
|
nouveaux enums evolutifs maintenus #[non_exhaustive]
|
||||||
|
absence de nouveau module production dans ksp-store-api
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation de l'assemblage
|
||||||
|
|
||||||
|
L'environnement d'assemblage ne fournit ni `cargo`, ni `rustc`, ni `rustfmt`. Aucun gate Rust n'est donc déclaré PASS localement.
|
||||||
|
|
||||||
|
Ont été vérifiés localement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
inventaire exact du delta
|
||||||
|
workspace.package.version = 0.3.16-pre.2
|
||||||
|
headers file/version des fichiers modifiés
|
||||||
|
newline finale
|
||||||
|
lignes Rust <= 160 caractères
|
||||||
|
absence de lockfile/cache/secret
|
||||||
|
cohérence statique exports <-> release_completeness
|
||||||
|
cohérence du plan 038 avec les noms Rust désormais figés
|
||||||
|
```
|
||||||
|
|
||||||
|
## Gate requis après application
|
||||||
|
|
||||||
|
Comme cette tranche modifie du Rust et le manifest workspace, exécuter :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fmt --all
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
|
||||||
|
python3 scripts/audit_rust_workspace_rules.py
|
||||||
|
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas
|
||||||
|
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
cargo test -p ksp-store-api --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
Ne pas exécuter `npm run build` pour ce gate ; aucune application Desk n'est modifiée.
|
||||||
|
|
||||||
|
## Prochaine tranche
|
||||||
|
|
||||||
|
Après gate propre :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.3.16-pre.003
|
||||||
|
```
|
||||||
|
|
||||||
|
Objet prévu : migration PostgreSQL V003, registre/resources et schéma minimal variant ledger + selector + conflit, avec canaris garantissant l'immuabilité byte/checksum des migrations V000/V001/V002.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/plans/038-V0_3_16_RAW_RESILIENCE_CONFLICT_PLAN.md -->
|
<!-- file: docs/plans/038-V0_3_16_RAW_RESILIENCE_CONFLICT_PLAN.md -->
|
||||||
<!-- version: 2 -->
|
<!-- version: 3 -->
|
||||||
|
|
||||||
# Plan `0.3.16` -> `0.3.18` — résilience RAW, variantes, conflits et récupération
|
# Plan `0.3.16` -> `0.3.18` — résilience RAW, variantes, conflits et récupération
|
||||||
|
|
||||||
@@ -648,7 +648,7 @@ Décision d'ownership :
|
|||||||
- `ksp-raw-transaction-lib` reste l'autorité sur la canonicalisation RAW v1 et fournit les golden/canaris utiles, sans devenir propriétaire de la politique Store de convergence ;
|
- `ksp-raw-transaction-lib` reste l'autorité sur la canonicalisation RAW v1 et fournit les golden/canaris utiles, sans devenir propriétaire de la politique Store de convergence ;
|
||||||
- le Worker peut utiliser la relation à titre de métrique/optimisation, mais le Store verrouillé reste l'autorité de la décision durable.
|
- le Worker peut utiliser la relation à titre de métrique/optimisation, mais le Store verrouillé reste l'autorité de la décision durable.
|
||||||
|
|
||||||
La tranche API devra encore vérifier le graphe Cargo avant de fixer le fichier/module Rust exact, mais la frontière de crate est fermée : le comparateur backend-neutral appartient à `ksp-store-api`. Aucune nouvelle dépendance circulaire n'est admise.
|
`0.3.16-pre.002` fixe les contrats de variante, relation, reason code et outcome dans les homes privés existants `model/raw_transaction.rs` et `model/raw_outcome.rs`, réexportés depuis la racine de `ksp-store-api`. Le comparateur pur lui-même reste réservé à `0.3.16-pre.005` et devra utiliser ces contrats sans introduire de nouvelle dépendance circulaire.
|
||||||
|
|
||||||
## 12. Rétention, archive et rehydration
|
## 12. Rétention, archive et rehydration
|
||||||
|
|
||||||
@@ -709,7 +709,7 @@ Le statut est volontairement petit. Le détail de la décision vit dans le journ
|
|||||||
|
|
||||||
## 14. Outcomes Store cibles
|
## 14. Outcomes Store cibles
|
||||||
|
|
||||||
Les noms Rust exacts seront figés dans `pre.002`, mais les catégories suivantes sont requises :
|
`0.3.16-pre.002` fige l'enum public `RawTransactionVariantWriteOutcome` avec les variantes suivantes :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
InsertedCanonical
|
InsertedCanonical
|
||||||
@@ -955,7 +955,7 @@ Gate d'audit, architecture, sizing, plan et validation. Cette tranche est déjà
|
|||||||
|
|
||||||
#### `0.3.16-pre.002`
|
#### `0.3.16-pre.002`
|
||||||
|
|
||||||
Contrats Store API backend-neutral : variant identity, relation de qualité, outcomes de persistance, reason codes et contrats minimaux nécessaires au conflit durable.
|
Contrats Store API backend-neutral : variant identity, relation de qualité, outcomes de persistance, reason codes et contrats minimaux nécessaires au conflit durable. Implémentation livrée par cette tranche ; gate opérateur requis avant `pre.003`.
|
||||||
|
|
||||||
#### `0.3.16-pre.003`
|
#### `0.3.16-pre.003`
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user