# Delta `0.3.14-pre.009` — health policy multi-source ## Base requise ```text 0.3.14-pre.008-fix.002 workspace.package.version = 0.3.14-pre.8.fix.2 deltas/0.3.14/pre.008-fix.002.md présent ``` ## Gate de la base Le gate opérateur de `0.3.14-pre.008-fix.002` est validé avant ouverture de cette tranche : ```text cargo fmt --all : PASS cargo fmt --all -- --check : PASS audit Rust workspace rules : PASS audit Markdown tables : PASS cargo check --workspace : PASS cargo clippy --workspace --all-targets --all-features -- -D warnings : PASS cargo test -p ksp-worker-raw-transaction-ingest-lib --all-targets --all-features : PASS ``` Le gate Worker comprend notamment : ```text 139 unit tests : PASS cross_layer_completeness : 8 PASS dependency_boundary : 19 PASS hardening : 33 PASS public_api : 20 PASS release_completeness : 10 PASS ``` ## Objectif Implémenter strictement la tranche `pre.009` du plan `035` : ```text projeter Healthy / Degraded / Unhealthy depuis des preuves de coverage présentes et futures conserver Faulted comme état lifecycle terminal du Worker ne jamais rendre Healthy sur le seul fait qu'un Transport ait reconnecté rendre Healthy uniquement lorsque toutes les sources attendues sont Active et que les gaps sont fermés rendre Degraded lorsqu'une source reste perdue mais que le présent est réconcilié et TargetCoverage reste couvert pour la suite rendre Unhealthy pendant une réconciliation non prouvée, avec gap ouvert ou coverage future insuffisante ne créer aucune health source/provider spécifique publique ``` ## Projection de health fondée sur la continuité Le ledger de `pre.008` expose maintenant une projection privée et source-neutral : ```text continuity_frontier has_open_gaps future_target_coverage failed_source_losses_reconciled ``` Pour chaque source projetée `Failed`, `failed_source_losses_reconciled` exige un gap `SourceFailure` effectivement enregistré puis fermé par une preuve run-local. Une source simplement marquée `Failed` dans l'inventaire ne suffit donc jamais à autoriser `Degraded`. `future_target_coverage` est calculé uniquement depuis les sources réellement `Active` et les relations `Exact` / `Superset` déjà validées par `TargetCoverage`. La configuration d'une source ne constitue toujours pas une preuve historique. Le présent reste gouverné par : ```text aucun gap ouvert continuity frontier == processing frontier ``` Les deux dimensions sont volontairement distinctes : ```text coverage future disponible != gap historique réparé gap historique réparé != coverage future encore disponible ``` ## Pont Inventory -> Continuity -> Snapshot Chaque publication agrégée de l'inventaire multi-source calcule désormais, après libération du lock Inventory : ```text ensemble exact des sources Active processing frontier agrégé projection continuity/TargetCoverage sous le lock continuity séparé projection health privée ajoutée au message latest-value ``` Aucun `MutexGuard` n'est conservé à travers un `await` et aucun client Transport/Store n'est exposé dans la snapshot publique. La source HTTP full-ledger republie sa projection après l'enregistrement d'un coverage epoch afin qu'une fermeture de gap puisse mettre à jour la health sans attendre une transaction ou un slot produit supplémentaire. ## Politique Running Lorsque la projection de continuité est disponible, la health suit la politique conservative suivante : ```text source Reconnecting -> Unhealthy tant que la continuité de l'incident n'est pas explicitement réconciliée gap ouvert ou continuity frontier != processing frontier ou TargetCoverage future non couverte par les sources Active -> Unhealthy toutes les sources attendues Active + aucun gap ouvert + continuity frontier rattrapé + TargetCoverage future couverte -> Healthy une source reste perdue/Failed + son gap SourceFailure a été enregistré et réconcilié + aucune source Reconnecting + aucun gap ouvert + continuity frontier rattrapé + TargetCoverage future couverte -> Degraded ``` Une source perdue n'est donc jamais transformée en source optionnelle. `Degraded` indique uniquement que le service du run reste couvert malgré son absence ; les observations spécifiques que cette source aurait produites ne sont pas inventées. ## Faulted reste un état lifecycle `ksp-worker-api::WorkerHealth` reste inchangé et conserve : ```text Unknown Healthy Degraded Unhealthy ``` La quatrième politique terminale du plan est représentée par : ```text WorkerState::Faulted(error_code) WorkerHealth::Unhealthy ``` Aucune nouvelle variante `WorkerHealth::Faulted` ni extension de l'API générique Worker n'est introduite. ## Retour à Healthy Le retour à `Healthy` exige simultanément : ```text toutes les sources attendues projetées Active aucune source projetée Reconnecting aucun gap run-local ouvert continuity frontier == processing frontier TargetCoverage future couverte par les sources Active ``` Le simple passage Transport `Reconnecting -> Active` ne constitue pas une preuve suffisante si le continuity ledger n'est pas réconcilié. ## Tests ajoutés Les unit tests couvrent notamment : ```text projection health séparant future TargetCoverage et gap historique ouvert rejet des ensembles Active dupliqués ou inconnus pont Inventory -> continuity health avec gap puis coverage epoch de réconciliation Reconnecting -> Unhealthy tant que la preuve de continuité manque sources Active + gap ouvert -> Unhealthy sources Active + gaps fermés + coverage future -> Healthy source Failed sans gap SourceFailure réconcilié -> Unhealthy source Failed avec gap SourceFailure réconcilié et présent/futur couverts -> Degraded publication supervisor immédiate après réconciliation réussie de la perte de source coverage future insuffisante -> Unhealthy WorkerState::Faulted -> WorkerHealth::Unhealthy ``` Les canaris `hardening` et `release_completeness` vérifient en plus : ```text health fondée sur continuity frontier + gaps + TargetCoverage aucune health provider/source-key spécifique publique aucune variante WorkerHealth::Faulted ajoutée aucune croissance de la surface publique TargetCoverage ``` ## Hors périmètre inchangé ```text aucune fairness spécifique nominal/repair de pre.010 aucune observabilité publique détaillée des gaps/repair de pre.011 aucun nouveau mécanisme de replay/reconnect Worker aucun respawn de source aucun EARLY/shred aucun Job Backfill depuis Worker aucun nouveau provider ou SDK ``` ## Fichiers ajoutés ```text deltas/0.3.14/pre.009.md ``` ## Fichiers modifiés ```text Cargo.toml crates/ksp-worker-raw-transaction-ingest-lib/src/continuity.rs crates/ksp-worker-raw-transaction-ingest-lib/src/runtime_resources.rs crates/ksp-worker-raw-transaction-ingest-lib/src/snapshot.rs crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/continuity.rs crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/snapshot.rs ``` ## Fichiers supprimés ```text aucun ``` ## Version Cargo Conformément à `VER-ID-009` : ```text header Cargo.toml : 578 -> 579 workspace.package.version : 0.3.14-pre.8.fix.2 -> 0.3.14-pre.9 ``` Versions des fichiers modifiés : ```text continuity.rs : 8 -> 9 runtime_resources.rs : 36 -> 37 snapshot.rs : 5 -> 6 unit_tests/continuity.rs : 6 -> 7 unit_tests/runtime_resources.rs : 30 -> 31 unit_tests/snapshot.rs : 5 -> 6 tests/hardening.rs : 32 -> 33 tests/release_completeness.rs : 27 -> 28 ``` ## Validation exécutée dans l'environnement de préparation ```text python3 scripts/audit_rust_workspace_rules.py : PASS python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas : PASS scan des frontières Worker/Transport/Backfill/Store : PASS scan de la crate-root historique : PASS comparaison exacte pre.008-fix.002 -> pre.009 : PASS ``` Les gates Cargo ne sont pas déclarés PASS dans l'environnement de préparation lorsqu'ils ne peuvent pas y être exécutés. Ils restent obligatoires côté opérateur avant `pre.010`. ## Décisions prises ```text la health est une projection de preuves run-local, pas un statut provider le futur est prouvé par TargetCoverage sur les sources Active le présent est prouvé par continuity frontier + absence de gap ouvert Reconnecting reste Unhealthy tant que l'incident n'est pas réconcilié Failed ne devient Degraded qu'après enregistrement et fermeture effective de son gap SourceFailure une source perdue mais réellement redondante maintient Degraded, jamais Healthy Faulted reste un WorkerState terminal avec WorkerHealth::Unhealthy ```