# Delta `0.3.6-pre.003` — notifications Job latest-value génériques ## Base requise ```text 0.3.6-pre.002-fix.001 workspace.package.version = 0.3.6-pre.2.fix.1 SHA-256 archive = 0b6640155786b92e27508637af2bcc7a0a34e5e8e4185d76ac4dd6d71aa7a869 ``` Le gate opérateur fourni pour cette base confirme `cargo fmt`, audits Rust/Markdown, `cargo check --workspace`, Clippy, les dix tests unitaires et onze canaries `ksp-job-api`, les suites individuelles, `cargo test --workspace`, les arbres Cargo Job API et l'examen des doublons. ## Objectif Matérialiser la tranche `pre.003` du plan 027 : observation Job latest-value runtime-neutral avec séquence monotone, snapshot complet courant, attente abstraite d'une valeur plus récente et resynchronisation multi-listeners, sans ouvrir encore `ksp-job-backfill-lib` ni imposer Tokio à l'API. ## Audit des archives et des règles Les deux archives fournies ont été testées avant modification : ```text KSP 0.3.6-pre.002-fix.001 SHA-256 = 0b6640155786b92e27508637af2bcc7a0a34e5e8e4185d76ac4dd6d71aa7a869 1541 entrées / aucune duplication / aucune traversée / aucun chemin absolu / aucun lien symbolique ZIP unzip -t = propre kbot3 v0.5.3-pre.005-fix010 SHA-256 = ee47643b9f8b582ee8db97b2381ec107e45aef8c009fee44757531514615d318 2501 entrées / aucune duplication / aucune traversée / aucun chemin absolu / aucun lien symbolique ZIP unzip -t = propre ``` La base KSP ne contient ni `Cargo.lock`, ni `.env`, ni `target`, ni `node_modules`, ni secret ou artefact de build détecté par l'inventaire statique. L'archive kbot3 reste une référence historique et contient notamment un `scripts/__pycache__`; ce contenu n'est ni copié ni livré. Les autorités relues incluent `RULES.md`, `docs/000-README.md`, les règles sous `docs/rules/`, les architectures Layers/Dependencies, Component Contracts, Dependency Graph et Acquisition/Workers/Jobs, le prompt 025, le plan 027, la validation 023 et les deltas `pre.001` à `pre.002-fix.001`. Pour cette tranche, kbot3 confirme uniquement le besoin fonctionnel historique : `BackfillObserver::on_progress` appelait directement le consumer applicatif et exposait timestamp, niveau, message libre et compteurs optionnels. KSP conserve l'observabilité mais rejette cette forme callback/producteur ; aucun code, DTO, ID, message ou runtime kbot3 n'est repris. ## Surface ajoutée `JobNotificationSequence` : - position initiale contrôlée par l'API ; - progression unitaire par `checked_add` ; - ordre total et test `is_after` ; - erreur stable `job_api.job_notification_sequence_exhausted` avant tout wrap. `JobNotification` : - `JobId`, `JobKindCode`, séquence, `JobState` et snapshot complet ; - champs privés et valeur immutable ; - `Debug` structurel qui masque toujours le snapshot générique. `JobSnapshotFuture<'a, S>` et `JobSnapshotSource` : - futur abstrait fondé uniquement sur `std::future::Future`, `Pin` et `Box` ; - lecture du snapshot courant ; - attente d'une valeur plus récente après une séquence observée ; - coalescence autorisée des progressions intermédiaires, avec resynchronisation sur le snapshot complet courant ; - aucun callback producteur, channel Tokio, event log, Tauri ou historique public. La source runtime concrète n'est pas créée dans Job API. Elle restera possédée par `ksp-job-backfill-lib` lorsque cette crate ouvrira ; les canaries de `pre.003` prouvent le contrat externe, sans annoncer comme acquises les propriétés runtime O(1), wake-up ou isolation du producteur avant ce consumer réel. ## Tests ajoutés Trois unitaires : - progression stricte et ordre de séquence ; - épuisement `u64::MAX` explicite sans wrap ; - conservation de la valeur et redaction du snapshot en `Debug`. Trois canaries externes : - consommation de l'enveloppe depuis le crate-root ; - deux listeners indépendants resynchronisés vers la dernière valeur après séquences anciennes différentes ; - snapshot terminal encore lisible depuis un handle partagé. Les canaries de dépendances et de complétude existantes sont étendues pour couvrir `notification.rs`, les nouveaux exports et l'absence de fuite Backfill/Worker/runtime. ## Fichiers ajoutés ```text crates/ksp-job-api/src/notification.rs crates/ksp-job-api/tests/notifications.rs crates/ksp-job-api/unit_tests/notification.rs deltas/0.3.6/pre.003.md ``` ## Fichiers modifiés ```text Cargo.toml crates/ksp-job-api/src/error.rs crates/ksp-job-api/src/lib.rs crates/ksp-job-api/tests/dependency_boundary.rs crates/ksp-job-api/tests/release_completeness.rs docs/plans/027-V0_3_6_JOB_API_BACKFILL_PLAN.md docs/validation/023-V0_3_6_JOB_API_BACKFILL.md ``` Mécanique Cargo : ```text header version: 396 -> 397 workspace.package.version: 0.3.6-pre.2.fix.1 -> 0.3.6-pre.3 workspace members: inchangés workspace dependencies: inchangées ``` ## Fichiers supprimés Aucun. ## Décisions prises - une séquence publique ne se construit pas depuis un entier arbitraire ; elle part de `initial()` puis progresse via `next()` ; - `JobNotification` reste générique afin que Job API ne possède aucun snapshot Backfill prématuré ; - le `Debug` de l'enveloppe ne demande pas `S: Debug` et ne rend jamais le snapshot ; - le contrat d'attente expose un futur `std` boxed plutôt qu'un type Tokio ou un callback ; - la sémantique publique est latest-value, pas une queue et pas un event log durable ; - une séquence manquée n'exige aucun replay : le listener reprend le snapshot complet le plus récent ; - la source concrète et son wake-up ne sont pas simulés comme fonctionnalité de production dans Job API ; ils seront prouvés avec le premier consumer réel ; - `ksp-job-api` conserve exactement sa dépendance normale Core-only et aucune feature/dev-dependency/build-dependency n'est ajoutée ; - aucun README/USAGE intermédiaire n'est créé avant la surface consommable réconciliée prévue par le plan. ## Validations exécutées ```text unzip -tq des deux archives -> propre inventaire ZIP -> KSP: 1541 entrées, 0 duplicate, 0 traversal, 0 absolute, 0 symlink -> kbot3: 2501 entrées, 0 duplicate, 0 traversal, 0 absolute, 0 symlink python3 scripts/audit_rust_workspace_rules.py -> General Rust rule audit: clean -> Rust export completeness audit: 0 candidate(s) -> KSP workspace Rust rule audit: clean python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.6 -> Markdown table audit: clean (264 tables / 144 fichiers) python3 -m unittest scripts/tests/test_audit_markdown_tables.py -> 5 tests / OK inventaire statique ksp-job-api -> 13 tests unitaires -> 14 canaries d'intégration ``` L'inventaire différentiel base/travail confirme que seuls les fichiers listés par ce delta sont ajoutés ou modifiés, hors caches Python temporaires supprimés avant assemblage. ## Validations non exécutées L'environnement d'assemblage ne fournit ni `cargo` ni `rustfmt`. Les commandes Rust de `pre.003` ne sont donc pas annoncées comme réussies localement. Le smoke réseau et PostgreSQL ne concernent pas cette tranche API-only. ## Gate opérateur demandé ```bash cargo fmt --all python3 scripts/audit_rust_workspace_rules.py python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.6 cargo check --workspace cargo clippy --workspace --all-targets cargo test -p ksp-job-api cargo tree -p ksp-job-api --edges normal cargo tree -p ksp-job-api -e features ``` Le résultat attendu pour `ksp-job-api` après cette tranche est treize tests unitaires et quatorze canaries d'intégration, sans nouvelle dépendance normale ou feature. ## Questions ouvertes Aucune question ne bloque `pre.004` après un gate opérateur vert de `pre.003`.