<!-- file: docs/prompts/PROMPT_0_7_58_sqlite_db_transaction_merger_binary.md -->

# Prompt — 0.7.58 Binaire de fusion de bases SQLite transactionnelles + validation anti-régression inter-DEX

## Contexte

Nous reprenons le workspace Rust `khadhroony-bobobot` après :

```text
0.7.57 -> meteora_dlmm clos
0.7.57-pre.015..023 -> nettoyage PumpSwap / guard PumpFees après régression cross-surface
```

Décision de planification mise à jour :

```text
0.7.58 -> sqlite_db_transaction_merger
0.7.59 -> demo4_program_surface_discovery
0.7.60 -> meteora_damm_v1
0.7.61 -> meteora_damm_v2
```

Le binaire de fusion passe avant `demo4`, car il devient un outil de non-régression indispensable pour les futurs décodeurs/materializers.

## Objectif principal

Créer un outil binaire de fusion de bases SQLite transactionnelles permettant de construire une base consolidée de corpus, par exemple `final.db`, à partir :

```text
final.db existante
+ base dédiée pump_swap.db
+ base dédiée pump_fun.db
+ base dédiée pump_fees.db
+ base dédiée raydium_*.db
+ base dédiée meteora_*.db
```

Le binaire doit fusionner les transactions/instructions déjà backfillées sans RPC, sans redécoder et sans matérialiser pendant le merge.

Ensuite, la base consolidée doit être rejouable localement avec :

```text
metadata=no
skipDexDecode=no
forceDexDecode=yes
deferInstructionObservations=yes
```

But : détecter les régressions inter-DEX comme celle observée où un décodage `pump_fees` tronqué interrompait la matérialisation `pump_swap.buy_exact_quote_in` dans la même transaction.

## Règle de workflow future

Pour chaque nouveau DEX ou nouvelle surface :

1. Créer une base vide dédiée au DEX en cours.
2. Backfiller uniquement les signatures/pools nécessaires pour ce DEX.
3. Développer le decoder/materializer sur cette base dédiée.
4. Clore avec les SQL propres du DEX.
5. Fusionner la base dédiée dans `final.db` ou dans une copie `final.next.db`.
6. Rejouer `final.next.db` avec `forceDexDecode=yes`.
7. Lancer les validations globales anti-régression sur Pump/Raydium/Meteora et sur les surfaces closes.
8. Promouvoir `final.next.db` en `final.db` seulement si les gates sont propres.

Commande conceptuelle :

```bash
cargo run -p kb_tools --bin kb_db_merge --   --output ./final.next.db   --input ./final.db   --input ./corpus/pump_swap.db   --input ./corpus/pump_fees.db   --mode raw-corpus   --dry-run
```

Puis, après validation dry-run :

```bash
cargo run -p kb_tools --bin kb_db_merge --   --output ./final.next.db   --input ./final.db   --input ./corpus/new_dex.db   --mode raw-corpus   --replace-output
```

## Emplacement recommandé

Créer un package outil séparé :

```text
kb_tools/
  Cargo.toml
  src/bin/kb_db_merge.rs
```

Ajouter `kb_tools` dans le workspace principal.

Alternative acceptable si la structure workspace rend cela plus simple :

```text
kb_lib/src/bin/kb_db_merge.rs
```

Mais privilégier `kb_tools` pour éviter de mélanger outil CLI et librairie métier.

## Contraintes de code

Respecter les contraintes projet :

- Rust 2024 ;
- async-first si accès DB async ;
- tracing obligatoire ;
- pas de `?` dans le code applicatif ;
- pas de `unwrap` / `expect` hors tests ;
- pas de `anyhow` / `thiserror` ;
- pas de `mod.rs` ;
- pas de `pub mod`, utiliser `mod` + `pub use` ;
- imports directs seulement pour les traits ;
- tests offline ;
- types DB sous `kb_lib/src/db/entities/` et `kb_lib/src/db/dtos/`, pas sous `queries/` ;
- si des requêtes DB sont ajoutées/modifiées : mettre à jour les re-exports dans `kb_lib/src/db.rs` puis `kb_lib/src/lib.rs`.

## Modes CLI

Modes initiaux :

```text
raw-corpus
signatures-only
full-copy-safe
```

Le mode par défaut doit être :

```text
raw-corpus
```

### `raw-corpus`

Copier uniquement les données nécessaires à un replay local fiable :

```text
k_sol_chain_transactions
k_sol_chain_instructions
```

Ne pas copier les tables matérialisées par défaut :

```text
k_sol_dex_decoded_events
k_sol_trade_events
k_sol_liquidity_events
k_sol_pool_lifecycle_events
k_sol_pair_candles
k_sol_fee_events
k_sol_reward_events
k_sol_pool_admin_events
k_sol_orderbook_events
k_sol_token_account_events
k_sol_instruction_observations
k_sol_dex_event_coverage_entries
```

Raison : ces tables dépendent de la version de decoder/materializer et doivent être reconstruites par replay local.

### `signatures-only`

Créer une table de staging de signatures/slots/source, sans copier les transactions complètes. Ce mode est utile pour audit ou pour préparer des backfills contrôlés.

### `full-copy-safe`

Mode optionnel, non prioritaire.

Copier aussi des lignes décodées/matérialisées uniquement si :

- le schema version est compatible ;
- la version logique de decoder est identique ;
- toutes les foreign keys peuvent être remappées ;
- le mode est explicitement demandé.

Ne jamais en faire le mode par défaut.

## Options CLI minimales

```text
--output <path>
--input <path>        répétable
--mode <raw-corpus|signatures-only|full-copy-safe>
--dry-run
--replace-output
--source-label <label> répétable ou inféré depuis le fichier
--limit <n>            optionnel pour tests
--batch-size <n>       optionnel
```

Options utiles pour `final.db` :

```text
--allow-existing-output
--merge-into-copy-of <path>
--conflict-policy <record|prefer-complete|fail>
```

Par défaut : ne pas écraser l’output existant sans `--replace-output` ou `--merge-into-copy-of`.

## Politique de déduplication

Identité canonique :

```text
signature
```

Règles :

- Une signature déjà présente dans l’output ne doit pas être dupliquée.
- Si `slot`, `err_json`, `transaction_json`, `meta_json` ou les instructions diffèrent, enregistrer un conflit.
- Ne pas écraser silencieusement.
- Si une version est plus complète, ne la préférer que si la politique `prefer-complete` est demandée et que le conflit est journalisé.
- Garder une provenance complète source -> output.

Tables suggérées :

```text
k_sol_db_merge_sources
k_sol_db_merge_transactions
k_sol_db_merge_conflicts
```

Champs source suggérés :

```text
source_id
source_path
source_label
source_schema_version
source_created_at
input_transaction_count
copied_transaction_count
skipped_duplicate_count
conflict_count
```

Champs transaction de merge suggérés :

```text
signature
slot
source_id
source_transaction_id
output_transaction_id
copy_status
conflict_status
copied_at
```

## Compatibilité de schéma

Le binaire doit inspecter chaque input via :

```sql
SELECT name, sql FROM sqlite_master WHERE type = 'table';
PRAGMA table_info(k_sol_chain_transactions);
PRAGMA table_info(k_sol_chain_instructions);
```

Pour `raw-corpus`, refuser proprement une DB qui ne contient pas :

```text
k_sol_chain_transactions
k_sol_chain_instructions
```

ou les colonnes nécessaires au replay local.

Le message d’erreur doit indiquer :

```text
source DB
missing table
missing column
mode demandé
```

## Stratégie de copie

Algorithme recommandé :

1. Créer ou initialiser l’output avec le schéma courant `kb_lib`.
2. Enregistrer chaque input dans `k_sol_db_merge_sources`.
3. Lire les transactions source par slot/signature.
4. Pour chaque transaction :
   - si signature absente de l’output : insérer transaction + instructions enfants ;
   - si signature présente : comparer les champs canoniques ;
   - si identique : enregistrer duplicate skipped ;
   - si différent : enregistrer conflit.
5. Remapper `source_transaction_id -> output_transaction_id` pour les instructions.
6. Commit par batch.
7. Imprimer un résumé final.

## Validation anti-régression intégrée à 0.7.58

Ajouter une documentation et, si possible, un script SQL :

```text
validation_sql/SQL_VALIDATION_DB_MERGE_0_7_58.sql
validation_sql/SQL_VALIDATION_CROSS_DEX_REGRESSION_0_7_58.sql
```

Checks merge minimaux :

```sql
-- duplicate signatures in output should be empty
SELECT signature, COUNT(*)
FROM k_sol_chain_transactions
GROUP BY signature
HAVING COUNT(*) > 1;

-- orphan instructions should be empty
SELECT ins.id
FROM k_sol_chain_instructions ins
LEFT JOIN k_sol_chain_transactions tx ON tx.id = ins.transaction_id
WHERE tx.id IS NULL;

-- conflicts summary
SELECT *
FROM k_sol_db_merge_conflicts
ORDER BY id DESC
LIMIT 100;
```

Checks anti-régression DEX après replay de `final.next.db` :

```sql
-- successful decoded events without any materialized target
SELECT
    de.protocol_name,
    de.event_kind,
    json_extract(de.payload_json, '$.eventActionability') AS event_actionability,
    json_extract(de.payload_json, '$.skipTradeReason') AS skip_trade_reason,
    COUNT(*) AS missing_count,
    MIN(tx.signature) AS sample_signature
FROM k_sol_dex_decoded_events de
JOIN k_sol_chain_transactions tx ON tx.id = de.transaction_id
LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = de.id
LEFT JOIN k_sol_liquidity_events lie ON lie.decoded_event_id = de.id
LEFT JOIN k_sol_pool_lifecycle_events ple ON ple.decoded_event_id = de.id
LEFT JOIN k_sol_fee_events fee ON fee.decoded_event_id = de.id
LEFT JOIN k_sol_reward_events rew ON rew.decoded_event_id = de.id
LEFT JOIN k_sol_pool_admin_events adm ON adm.decoded_event_id = de.id
LEFT JOIN k_sol_orderbook_events obe ON obe.decoded_event_id = de.id
LEFT JOIN k_sol_token_account_events tae ON tae.decoded_event_id = de.id
WHERE (tx.err_json IS NULL OR tx.err_json = '' OR tx.err_json = 'null')
  AND te.id IS NULL
  AND lie.id IS NULL
  AND ple.id IS NULL
  AND fee.id IS NULL
  AND rew.id IS NULL
  AND adm.id IS NULL
  AND obe.id IS NULL
  AND tae.id IS NULL
GROUP BY de.protocol_name, de.event_kind, event_actionability, skip_trade_reason
ORDER BY missing_count DESC, de.protocol_name, de.event_kind;
```

Cette requête ne doit pas forcément être vide, mais tout résidu doit être explicitement classé : failed-only, decoded-only justifié, non_actionable prouvé, ou nouveau bug.

## Vérification/correction Pump/Raydium incluse dans 0.7.58

La tranche `0.7.58` doit inclure un passage de non-régression sur les surfaces déjà closes, au minimum :

```text
pump_swap
pump_fun
pump_fees
raydium_cpmm
raydium_clmm
raydium_amm_v4
raydium_stable_swap
raydium_launchpad
meteora_dbc
meteora_dlmm
```

Objectif : détecter et corriger les régressions cross-surface, par exemple :

```text
un decoder A ne doit pas faire échouer la matérialisation d’un decoder B dans la même transaction.
```

Règle de decoder :

- un payload tronqué/incompatible sur une surface secondaire ne doit pas aborter toute la transaction si l’entrée peut être ignorée proprement ;
- préférer `Ok(None)` + observation/debug contrôlé pour les payloads tronqués connus ;
- conserver `Err` pour corruption critique, violation de schéma interne ou incohérence qui rend la transaction non fiable ;
- ajouter des tests synthétiques pour chaque guard.

## Tests attendus

Ajouter tests unitaires/offline pour :

1. merge d’une DB vers output vide ;
2. merge de deux DBs disjointes ;
3. duplicate signature identique ;
4. duplicate signature conflictuelle ;
5. conflit instruction enfant ;
6. dry-run sans écriture ;
7. output existant refusé sans option explicite ;
8. source provenance enregistrée ;
9. replay final DB possible après merge raw-corpus ;
10. non-régression PumpSwap/PumpFees : payload PumpFees tronqué n’empêche pas PumpSwap de matérialiser un trade exact.

## Livrables attendus

```text
kb_tools/Cargo.toml
kb_tools/src/bin/kb_db_merge.rs
kb_lib/src/db/entities/db_merge_source.rs
kb_lib/src/db/entities/db_merge_transaction.rs
kb_lib/src/db/entities/db_merge_conflict.rs
kb_lib/src/db/dtos/db_merge_*.rs
kb_lib/src/db/queries/db_merge_*.rs
validation_sql/SQL_VALIDATION_DB_MERGE_0_7_58.sql
validation_sql/SQL_VALIDATION_CROSS_DEX_REGRESSION_0_7_58.sql
docs/reports/SQLITE_DB_TRANSACTION_MERGER_0_7_58.md
```

Mettre à jour :

```text
Cargo.toml
kb_lib/src/db.rs
kb_lib/src/lib.rs
README.md
ROADMAP.md
CHANGELOG.md
```

## Critères de clôture 0.7.58

- `cargo test -p kb_lib` OK ;
- `cargo test -p kb_tools` OK si package séparé ;
- clippy `-D warnings` OK ;
- dry-run merge lisible ;
- merge réel de plusieurs bases OK ;
- pas de duplicate signatures ;
- pas d’instructions orphelines ;
- conflits journalisés ;
- replay local de `final.next.db` OK ;
- validation anti-régression Pump/Raydium/Meteora exécutée et documentée.

## Note finale

Le merger ne remplace pas `demo3` ni `demo4`.

Il sert à construire un corpus consolidé de non-régression à partir des bases dédiées. `demo4`, déplacé en `0.7.59`, servira ensuite à explorer les surfaces encore inconnues dans cette base consolidée.
