Skip to content

datadiff 0.6.0 : traitement des 24 issues ouvertes de l'audit#58

Merged
VincentGuyader merged 65 commits into
mainfrom
audit-0.4.10
Jul 12, 2026
Merged

datadiff 0.6.0 : traitement des 24 issues ouvertes de l'audit#58
VincentGuyader merged 65 commits into
mainfrom
audit-0.4.10

Conversation

@VincentGuyader

Copy link
Copy Markdown
Member

Cette MR integre la branche audit-0.4.10 : 23 issues traitees une a une (la 24e, #11, livree separement par ta PR #12, integree ici), chacune via sa propre PR reviewee (review interne + review GitHub Copilot systematique) et mergee apres verdict. R CMD check --as-cran : 0 erreur / 0 warning / 1 note environnementale (horloge). Suite de tests : 802 PASS au depart, 1028 PASS / 0 FAIL / 0 SKIP a l'arrivee. Aucun conflit avec main (0 commit de retard).

A lire en premier : breaking changes

  1. NA unilateral = echec partout ([bug] Divergence de verdict local vs lazy sur NA unilatéral pour les colonnes d'égalité (na_equal non homogène) #14, PR fix!: NA unilateral = difference sur les colonnes d'egalite du chemin local #40). Avec na_equal = TRUE (defaut), un NA face a une valeur passait sur le chemin local et echouait en lazy. Les trois implementations (kernel local, SQL, pointblank direct) sont alignees : unilateral = difference, bilateral = suit na_equal. Impact possible sur l'oracle enc.mco : des comparaisons de colonnes character avec NA unilateral qui passaient en 0.4.9 echoueront. Le garde d'equivalence a ete re-pinne sciemment.
  2. Cle absente = erreur franche ([bug] Retour anticipé « clé introuvable » : structure de résultat incomplète (6 champs au lieu de 8) qui casse les consommateurs aval #16, PR fix: erreur explicite quand une colonne cle est absente d'un dataset #35). Fini le retour tronque a 6 champs : erreur nommant les colonnes et le dataset, sur tous les chemins. Cle vide/non-character rejetee (avant : cross join silencieux).
  3. equal_mode: normalized agit ([API] equal_mode « normalized » est un bouton sans effet (4 paramètres *_equal_mode inertes dans write_rules_template) #27, PR fix: equal_mode normalized implique case_insensitive + trim sauf override #51). Il implique trim + case_insensitive sauf override explicite (le code s'aligne sur ce que la vignette documentait depuis toujours). Verdicts de YAML manuscrits concernes : bascules FAIL -> PASS possibles.
  4. Extraits d'echec : ne montrent plus les colonnes ignorees ([perf] Scans et transferts évitables : COUNT(*) systématiques, schéma collecté deux fois, colonnes ignorées embarquées dans le join, collect intégral des doublons #24), portent __eq en local ([bug] Divergence de verdict local vs lazy sur NA unilatéral pour les colonnes d'égalité (na_equal non homogène) #14) et de nouveau __absdiff/__thresh (Réintroduire les écarts mesurés (__absdiff / __thresh) dans les extraits d'échec, perdus depuis la 0.4.8 #11).

Bugs corriges (verdicts faux ou crashs)

Issue PR Correctif
#13 #41 NaN/Inf : le SQL genere reproduit le kernel R (faux PASS/FAIL sur Parquet contenant NaN/Inf)
#14 #40 Divergence NA unilateral local/lazy (breaking, voir plus haut)
#15 #36 Positionnel a effectifs inegaux : erreur franche + fix du cas mixte local/lazy
#16 #35 Cle introuvable : erreur explicite (breaking, voir plus haut)
#18 #38 Colonne utilisateur « n » : detection de doublons lazy faussee
#19 #39 Fuite de temp tables DuckDB sur connexion utilisateur + noms collisionnables
#20 #37 label ecrase, champ keys lu par partial matching
#28 #42 Colonnes factor jamais normalisees (+ crash niveaux differents)

Robustesse

Issue PR Correctif
#17 #43 Colonne __ok/__eq absente = erreur interne (all(NULL) etait un faux all-pass) ; a demasque un bug latent 0.4.8 (noms fantomes recycle0)
#29 #45 report.R : memoisation partagee, eval_error propage, constantes centralisees
#30 #44 SQL arrow echappe (chemins avec apostrophe), union_by_name, memory_limit valide

Performance

Issue PR Gain
#21 #47 Une seule passe sur les booleens (verdict/coverage/echecs derives de la meme table)
#22 #50 Verdict lazy par agregats SQL : cas vert sans AUCUN collect (~2 Go economises sur 4M x 125)
#23 #46 Fast-path NA-sans-Inf : ~2,6x par colonne porteuse de NA
#24 #48 COUNT(*) conditionnels, schema collecte 1x, join restreint, doublons agreges en SQL

API, docs, qualite

Issue PR Contenu
#10 #49 extracts_dir : extraits en vrais CSV (la webview Positron/Workbench bloque les data-URI) ; reponse postee sur l'issue
#25 #52 setup_pointblank_agent : cols_reference deprecie, steps d'egalite sur booleen derive (semantique NA partagee), exemple executable
#26 #53 Contradictions NEWS 0.4.4 resolues : %
#27 #51 equal_mode normalized effectif (breaking, voir plus haut)
#31 #57 Suite de tests : doublon supprime, plus d'ecriture cwd, test mort sorti (0 SKIP), assertions conditionnelles durcies, helpers testes en direct
#32 #55 @return exact, README Quick Start zero-config, section Dev sans sorties perissables, read_rules actionnable (version, typos, sections non-mapping)
#33 #54 RFC API 0.5 dans dev/RFC-api-0.5.md + synthese postee sur l'issue (restee ouverte pour discussion)
#34 #56 Menage : dev/ hors tarball, imports purges (note check stats eliminee), scories, wontfix motives dans NEWS

Processus qualite

  • TDD red-first sur chaque correctif : test ecrit avant le fix, rouge constate sur l'ancien code pour la bonne raison
  • Review interne (agent pr-reviewer) avant chaque push : verdict + matrice de couverture + verification empirique des equivalences
  • Review Copilot demandee sur chaque PR (23 PRs), chaque finding triage avec preuve : ~50 findings traites, 4 classes de faux positifs refutees par reprex (ordre on.exit, resolution namespace sous test_check, tempdir, regex), regles de review extraites et capitalisees dans l'agent interne
  • Les reviews croisees ont attrape 4 vrais bugs au-dela des issues : cross join sur cle vide, cas mixte local/lazy positionnel, verdict structurel-seul en lazy apres [perf] Chemin lazy : le collect() de la table slim booléenne charge N×(T+E) logicals en RAM — contredit la promesse « without loading data into R memory » #22, overflow as.integer au-dela de 2^31 sur les comptes SQL

Apres merge

infra-bot and others added 30 commits July 2, 2026 17:49
…(issue #16)

Le retour anticipe silencieux (liste tronquee a 6 champs sans coverage ni
summary) est remplace par un stop() qui nomme les colonnes cles manquantes
et le ou les datasets concernes. Le test qui verrouillait l'ancien
comportement est remplace par des expect_error/expect_match couvrant :
cle absente des deux cotes, cote candidat seul, sans YAML explicite,
et cle multi-colonnes partiellement absente.
Reponse a la review Copilot de la PR :
- la cle passee en argument est validee contre les DEUX datasets avant la
  branche path = NULL, qui atteignait write_rules_template() en premier et
  levait une erreur ne validant que la reference, dans un format different
- une cle vide (character(0)) ou non-character est rejetee proprement au
  lieu de tomber dans un cross join dplyr deprecie
- la validation est factorisee dans validate_comparison_key(), reutilisee
  par le check tardif qui couvre les cles issues du YAML
… (issue #15)

Sans cle et a effectifs differents, error_msg_no_key n'alimentait qu'un
message() informatif juste avant un crash R opaque sur l'affectation des
colonnes de reference. L'erreur porte desormais le texte de
error_msg_no_key complete des deux effectifs. Sur le chemin positionnel
valide, les colonnes de reference sont ajoutees en un bind unique au lieu
d'une boucle d'affectation par colonne.
…turn

Review interne pr-reviewer : APPROVE. Review Copilot : 3 findings triages (1 valide corrige : validation canonique de la cle avant la branche template auto ; 1 partiellement valide corrige : rejet cle vide/non-character ; 1 partiellement valide : partial matching keys, corrige dans la PR de l'issue #20 qui suit).
…n positionnel)

Reponse a la review Copilot de la PR :
- le stop() effectifs inegaux est remonte avant le collect des tables
  non-locales (les comptes sont deja connus) : plus de materialisation
  inutile ni de note collecting trompeuse juste avant l'erreur
- la garde de collect ne testait que data_reference_p : une comparaison
  positionnelle melant reference locale et candidat lazy sautait la
  collecte du candidat et echouait en aval (bug preexistant revele par
  la review)
- le test positionnel capture desormais les messages et verifie que le
  texte de l'erreur ne fuit pas sur le flux message
…el (issue #20)

- label explicite honore meme quand path est fourni (etait ecrase
  inconditionnellement par le YAML)
- champ keys lu explicitement via [[ au lieu du partial matching $ sur
  key ; fallback retrocompatible sur un champ key singulier ; keys gagne
  quand les deux sont presents
- cles issues du YAML coercees en character (unlist) avant validation
- table de precedence dans la doc roxygen et note dans la vignette
…ismatch

Review interne pr-reviewer : APPROVE (equivalence bind/boucle verifiee, fidelite tibble via bind_cols). Review Copilot : 2 findings triages et appliques (stop avant collect + garde non-locale des deux cotes, capture des messages dans le test) ; re-review Copilot sans nouveau commentaire.
Reponse a la review Copilot de la PR :
- label NA ou non scalaire rejete a l'entree avec un message clair
  (validate_label, aussi branche dans write_rules_template qui portait le
  meme pattern latent if (label == ""))
- le test keys-sans-partial-matching active warnPartialMatchDollar, sans
  quoi il passait aussi avec l'implementation regressee
- le doublon de test affaibli par un suppressWarnings global est fusionne
  dans ce test unique (capture de tous les warnings + assertion ciblee)
…te, muffle cible

Reponse a la seconde review Copilot de la PR :
- un label "" vaut absence de label explicite (meme convention que
  write_rules_template) et laisse gagner le label YAML dans la chaine de
  precedence
- la doc de precedence precise que sur path = NULL c'est le label du
  template auto (Comparison with default rules) qui s'applique, pas le
  defaut interne
- le test de non-partial-matching ne muffle plus que les warnings partial
  match, les autres restent audibles
…issue #18)

find_duplicate_keys() lazy comptait via dplyr::count() sans nom explicite :
avec une cle nommee n, count() stocke le compte dans nn et le filter(n > 1)
lisait les valeurs de la cle, faussant silencieusement la detection de
doublons et les comptes du warning. Les deux helpers lazy utilisent
desormais un nom de compte reserve (..datadiff_n) et globalVariables(n)
est supprime.

Note : contrairement au constat initial de l'issue, lazy_nrow() ne cassait
pas (count() sans groupe ne renomme pas son compte quand une colonne n
existe) ; le nom explicite y est applique par robustesse et le comportement
verrouille par test.
Review interne pr-reviewer : APPROVE. 3 passes Copilot : 6 findings tous valides, tous corriges (partial matching, label NA, label vide, doc du label auto-template, tests renforces) ; 4e passe sans commentaire. 3 nouvelles regles extraites (48, 51 + tightenings).
…ne cle

Reponse a la review Copilot de la PR : count(name = <colonne de
groupement>) remplace silencieusement la colonne de groupement par le
compte, reproduisant la confusion valeur/compte sur le nom reserve
lui-meme (les exemples du warning montraient le compte au lieu de la
valeur de cle). Le nom reserve est etendu jusqu'a differer de toutes
les colonnes cles.
Sur le chemin lazy avec connexion fournie par l'utilisateur, la table slim
booleenne materialisee par compute() n'etait jamais droppee et s'accumulait
jusqu'a la fermeture de la connexion. Elle est desormais supprimee en
on.exit (y compris en cas d'erreur en aval du compute). Le nom de table,
derive de l'horloge (%H%M%OS3), pouvait entrer en collision dans la meme
milliseconde ; il vient maintenant d'un compteur par processus + PID via
datadiff_tmp_table_name().
… local (issue #14)

Le chemin local passait tout NA (unilateral inclus) avec na_equal = TRUE
via na_pass, la ou le chemin lazy et le kernel tolerance echouent sur un
NA unilateral : memes donnees, verdicts opposes selon le backend. Les
trois implementations sont alignees sur la semantique du kernel :
unilateral = difference, bilateral = na_equal.

- eq_col_bool : semantique cible, idiome vectorise sans ifelse
- chemin d'echec local : __eq materialise pour les colonnes d'egalite en
  echec, le step pointblank valide le meme booleen que le verdict
  (na_pass ne peut pas exprimer cette semantique)
- garde d'equivalence enc.mco mis a jour : le NA unilateral character
  echoue desormais quel que soit na_equal
- nouveau test-na-equality-semantics.R : equivalence local/DuckDB sur
  NA unilateral, bilateral, ligne candidate sans correspondance,
  na_equal TRUE/FALSE
…13)

Le CASE WHEN ne gerait que NULL ; or NaN est un float ordinaire dans
DuckDB (NaN = NaN vrai, NaN > tout) : des Parquet contenant NaN/Inf
obtenaient des verdicts inverses en lazy (faux PASS sur NaN/Inf
unilateral, faux FAIL sur infinis de meme signe).

- case_tol : bilateral NA/NaN -> na_equal, unilateral NA/NaN -> FALSE,
  infinis de meme signe -> TRUE, infini restant -> FALSE, sinon
  comparaison finie (le cap du fp_correction non fini est structurel :
  la branche finie n'est atteinte qu'avec une reference finie)
- detection backend : isnan()/isinf() sur DuckDB ; SQLite (pas de
  stockage NaN, insere NULL) garde les regles NULL et detecte l'infini
  par comparaison a DBL_MAX
- colonnes d'egalite numeriques : regles NA nan-aware (eq_num_cols),
  alignees sur le chemin local ou is.na(NaN) est TRUE
- tests d'equivalence avec le kernel R comme oracle (grille complete
  NaN/Inf/NA x na_equal, DuckDB + SQLite, egalite numerique NaN,
  bout-en-bout reprex de l'issue)
Review interne : APPROVE (traduction .data verifiee, codetools propre, %d verifie). Copilot : 1 finding partially-valid applique (nom reserve collision-proof, exemples du warning corrects) ; re-review sans nouveau commentaire. Regle #52 extraite (hypothese documentee mais non gardee).
Review interne : APPROVE (ordre on.exit et erreur avalee Arrow verifies empiriquement). Copilot : 1 finding false-positive (claim LIFO sur on.exit, refute par reprex : FIFO, after = FALSE prepend). Calibration negative-training row 58.
…uality

Review interne : APPROVE (idiome verifie sur 6 classes de types, discrimination TDD verifiee). Copilot : premiere passe sans aucun commentaire. Breaking change documente (NA unilateral, impact potentiel oracles type enc.mco signale).
# Conflicts:
#	NEWS.md
#	R/compare_datasets_from_yaml.R
Reponse a la review Copilot de la PR :
- le test SQLite couvre desormais aussi la tolerance relative (une
  reference infinie ne doit pas laisser passer un candidat fini via un
  seuil infini, detection DBL_MAX)
- limitation connue documentee : la detection NaN n'est cablee que pour
  DuckDB (backend lazy teste) ; un backend non-DuckDB qui stocke NaN
  (PostgreSQL) garde les regles NULL-only sur ces valeurs
Review interne : APPROVE (5 branches du CASE tracees contre le kernel, DBL_MAX verifie sur RSQLite, grille 7x7 verifiee). Copilot : 2 findings (limitation NaN backends non-DuckDB documentee honnetement - pas de predicat NaN portable en SQL ; boucle rel-tol SQLite ajoutee) ; re-review sans nouveau commentaire.
detect_column_types() classe les factors en character (ils recoivent les
regles case_insensitive/trim) mais normalize_text() ne transformait que
les vecteurs is.character() : une colonne factor n'etait jamais
normalisee, sans avertissement - verdict different selon
stringsAsFactors ou un import haven. Par ailleurs == entre deux factors
a niveaux differents errait.

preprocess_dataframe() convertit les factors locaux en character avant
normalisation (les tables lazy ne portent pas de factors) et le predicat
is_char reconnait un factor dans le schema. Doc de detect_column_types
clarifiee (fourre-tout character intentionnel, list-columns non
supportees).
…umn_types

Reponse a la re-review Copilot de la PR (tournure agrammaticale dans la
doc generee).
…sue #17)

all(NULL) vaut TRUE : une colonne de validation silencieusement droppee
devenait un faux all-pass avec n = 0 dans la coverage - le pire mode de
casse pour un outil de non-regression.

- tol_col_bool / eq_col_bool : stop() explicite nommant la colonne
  manquante ; les reducteurs (passes/counts) heritent du garde
- projection slim lazy : all_of() (bruyant) au lieu de any_of() (drop
  silencieux)
- la pose du garde a immediatement demasque un bug latent :
  paste0(character(0), "__ok") produit "__ok" (recycle0 FALSE par
  defaut), un nom fantome present dans val_cols depuis la 0.4.8 et
  avale par any_of() ; gardes de longueur ajoutes
…sue #30)

- chemins Parquet quotes via DBI::dbQuoteString : un chemin contenant une
  apostrophe (l'export/, courant en francais) cassait la requete avec une
  erreur SQL brute ; nom de table via dbQuoteIdentifier
- read_parquet(union_by_name = true) : binding des fichiers par NOM de
  colonne, aligne sur la garantie de schema unifie du fallback
  arrow::to_duckdb() quand l'ordre physique differe entre fichiers
- duckdb_memory_limit valide comme litteral de taille avant interpolation
  dans SET (rejet des valeurs injectables ou invalides) ; SET
  temp_directory quote aussi
- tests dedies : chemin avec apostrophe bout-en-bout, schemas decales
  multi-fichiers, fallback to_duckdb, memory_limit invalide
Review interne : APPROVE (conversion cle sure, ordered factors, probe Arrow bout-en-bout). Copilot 2 passes : doc de la fonction exportee porteuse completee (regle #11 durcie), reformulation grammaticale appliquee.
Review interne : APPROVE (claim recycle0 verifie, propagation coverage testee). Copilot : 7 commentaires = 1 claim false-positive (internes a nu dans les tests sous R CMD check), refute par execution sous le harnais installe (test_dir load_package=installed) ; regle #36 recalibree avec discriminateur test_check/env_clone.
Reponse a la review Copilot de la PR : un nom de repertoire fixe sous
tempdir() peut entrer en collision entre runs paralleles ou avec les
restes d'un run precedent. Base unique via tempfile(), l'apostrophe
reste dans le nom feuille (la partie porteuse du test).
…antes centralisees (issue #29)

- datadiff_report_html() passe par datadiff_render_report() : lit le
  cache alimente par print() et l'alimente en retour (plus de double
  construction agent + rapport)
- un eval_error sur l'agent reel (n_failed NA) prend la branche reelle
  du build (le garde any(n_failed > 0) seul routait silencieusement vers
  la branche synthetique all-pass) et survit au bloc vectorise (qui
  forcait eval_error/eval_warning a FALSE sur toutes les lignes)
- bloc mort supprime dans build_report_agent : les injections de
  compteurs par ligne du else etaient integralement reecrites par le
  bloc vectorise authoritative-from-coverage
- R/constants.R : suffixes __ok/__eq, prefixes __missing_col_/
  __type_mismatch_ et defauts warn_at/stop_at definis une fois et
  partages par producteurs, consommateurs du verdict et mapping du
  rapport ; helpers datadiff_ok_col()/datadiff_eq_col()
infra-bot and others added 19 commits July 3, 2026 11:26
…its est documente

Reponse a la review Copilot : le contrat extracts_dir mentionne
desormais la substitution des caracteres hors [A-Za-z0-9_.-].
…chemin d'erreur

- report_underlying_col cherche le step par son id (match sur vs$i) et
  non par position de ligne : la contiguite du validation_set est un
  invariant implicite de pointblank, pas un contrat
- write.csv en fileEncoding UTF-8 (les extraits existent pour donner un
  fichier portable)
- test de regression du warning sur reponse illisible (rien n'ecrit,
  pas de repertoire cree)
Review interne : APPROVE (invariant d'alignement verifie empiriquement, memoisation independante confirmee, sentinel <row_count> assaini). Copilot 2 passes : 6 findings valides appliques. Nits internes appliques : lookup par id de step, UTF-8, test du chemin d'erreur. Rebase sur audit-0.4.10 courant avant merge.
…leens (issue #22)

Le collect() de la table slim chargeait N x (T+E) logicals en RAM R
(~2 Go pour 4M lignes x 125 colonnes sur le cas vert), contredisant la
promesse without loading data into R memory du commentaire - alors que
le verdict tient dans (T+E) paires (n, n_failed).

- lazy_boolean_counts() : un scan d'agregats SQL sur la temp table
  (SUM(CASE WHEN ok THEN 0 ELSE 1 END) compte FALSE et NULL, semantique
  exacte des reducteurs locaux ; SUM sur table vide -> NULL -> 0)
- build_coverage(counts =) : les comptes precalcules remplacent les
  accesseurs locaux sur le chemin lazy
- cas vert : plus aucun collect, l'agent placeholder est de taille
  constante (nrow(res$reponse$tbl) <= 1 au lieu de N)
- cas rouge : seules les colonnes booleennes EN ECHEC sont collectees
  pour l'agent (+ row_count_ok si check actif), extraits inchanges
- les helpers datadiff_ok_col/eq_col deviennent length-guarded : le
  fantome recycle0 (paste0(character(0), suffixe) == suffixe) a mordu
  une 2e fois via lazy_boolean_counts ; la garde centrale immunise la
  classe et le suffix_all local devient inutile
Review interne : APPROVE (equivalence agregats/reducteurs sur DuckDB + SQLite, cas degenere trace). Copilot 3 passes : 5 findings valides tous appliques, dont un vrai bug d'edge (echec structurel seul en lazy -> table vide -> steps dummy a 0 unite) et l'overflow as.integer au-dela de 2^31 rendu atteignable par les agregats. Cap de passes atteint.
…ride (issue #27)

Le champ n'etait lu qu'a un endroit ou identical(eq_mode, "normalized")
activait normalize_text(case_insensitive = FALSE, trim = FALSE) -
l'identite. Un utilisateur ecrivant equal_mode: normalized seul
n'obtenait rien, silencieusement, et test-utils verrouillait meme
l'inertie. La vignette documentait pourtant deja « Apply both
transformations » : le code s'aligne sur sa doc.

- normalized implique les deux normalisations pour les colonnes
  character, un flag explicite (meme FALSE) gagne sur le mode
- date/datetime/logical : *_equal_mode documentes acceptes-mais-inertes
  (la normalisation ne touche que character)
- test verrouillant l'inertie retourne + reprex de l'issue en test
Review interne : APPROVE (finding important applique : template auto-neutralisant). Copilot : premiere passe sans commentaire.
…sur booleen derive (issue #25)

- cols_reference jamais lu depuis son introduction : NULL par defaut +
  warning de depreciation quand fourni ; l'appelant pipeline passe en
  arguments nommes sans lui ; variables mortes cols_reference/
  cols_candidate supprimees de l'appelant
- les steps d'egalite locaux valident un booleen __eq derive en amont de
  create_agent (semantique NA partagee : unilateral echoue, bilateral
  suit na_equal) au lieu d'embarquer le vecteur de reference entier dans
  chaque step (O(lignes) par step, serialise avec le rapport, na_pass
  incapable d'exprimer la distinction) ; un cmp lazy sans __eq
  precalcule recoit une erreur de contrat explicite
- exemple roxygen executable et representatif (il ciblait b__ok
  inexistant : l'agent produit echouait a l'interrogation)
- get_col_names hisse hors boucle, calcule apres toutes les mutations
- tests : depreciation, semantique NA des appels directs (rouge avant),
  appels legacy du fichier de tests passes en arguments nommes
Review interne : APPROVE. Copilot : 2 nits valides appliques (expect_no_warning, doc du declencheur exact du warning).
…on, doc lang exacte (issue #26)

Le commit Perf (#1) post-0.4.4 avait re-exporte %||% (masquant
rlang::%||% et base::%||% R >= 4.4 au chargement), re-bascule le label
par defaut en francais et laisse la vignette pretendre un defaut
anglais - le tout sans entree NEWS, contredisant les annonces de la
0.4.4.

- %||% re-internalise (conforme NEWS 0.4.4) ; le test qui verrouillait
  l'export accidentel verrouille desormais l'inverse
- prefixe de label du template : comparison (annonce 0.4.4)
- vignette : defaut de langue reel documente (fr / fr_FR + options)
- normalize_text, validate_row_counts, setup_pointblank_agent
  documentes dans la section utilitaires de la vignette avec cas
  d'usage reels (ils n'apparaissaient nulle part)
Review interne : REQUEST CHANGES applique (narration %||%). Copilot : 2 findings valides appliques (span inline vignette, tempfile), 1 faux positif recurrent refute (resolution namespace sous test_check, cf PRs #43/#44).
Document de design a discuter, sans implementation : nom
compare_datasets() + alias retrocompatible, objets d'options
extract_opts()/engine_opts(), retour de classe datadiff_result
(passed/report, depreciation de reponse/all_passed dupliques),
fail_at unique, bascule lang en a la 0.5, cycle de depreciation en
3 versions. Les quick wins non cassants sont rattaches aux issues
deja traitees (#16/#20/#25/#26/#27/#30).
Document de design uniquement (hors tarball via ^dev$). Copilot : 2 findings valides appliques (annotation defauts 0.4.x, Rbuildignore embarque).
…ML (issue #32)

- @return complet (all_passed en tete, agent decrit fidelement : configure
  mais non interroge)
- README : Quick Start zero-config d'abord, cle explicite partout ; la
  section Dev n'embarque plus de sorties check/covr perissables (figees a
  la 0.4.2 depuis mars)
- vignette : restriction locale des diagnostics __absdiff/__thresh dans
  les extraits ; caveat NaN DuckDB-only du chemin lazy (releve au triage
  de la PR #41)
- read_rules : erreur de version explicite (fichier + version declaree +
  version supportee, fini le stopifnot brut) ; warning sur les champs
  inconnus racine/defaults (typos by_nmae, no_equal) en nommant les
  champs connus - la friction principale d'un YAML edite a la main
- write_rules_template refuse version != 1 en amont (il ecrivait des
  templates que read_rules refusait ensuite)
Review interne : APPROVE (liste known_defaults verifiee contre les 5 consommateurs reels). Copilot : 2 findings valides appliques en TDD (version non scalaire, sections non-mapping).
- .Rbuildignore ^dev$ (le RFC et les benchs ne partent plus dans le
  tarball) ; inst/templates/rules_template.yaml orphelin supprime
- imports : dplyr arrange/across retires, stats::setNames remplace par
  une construction base ; numeric_abs = 1e-9 lisible
- is_non_local reutilise is_arrow (liste de classes Arrow definie une
  fois) ; abs(ref_vals) et la constante IEEE hisses hors des kernels et
  boucles ; tol_col_counts compte sans allocations intermediaires ;
  format_key_examples tronque avant de formater
- preprocess_dataframe lazy : UNE seule mutate() composee pour toutes
  les colonnes normalisees (anti-pattern O(colonnes) dbplyr)
- variables de boucle c renommees (masquaient base::c) dans
  pointblank_setup, coverage, tolerance, compare
- note d'overflow integer documentee sur le kernel
- ecartes explicitement (wontfix motives dans NEWS) : rendu gt en session
  non interactive (print = rendu), garde DBI transitive via duckdb
Review interne : APPROVE (equivalences verifiees empiriquement). Copilot : 1 finding valide applique (note d'overflow reformulee : soustraction entiere, pas double).
- doublon octet pour octet supprime (test-key-parameter 249-293)
- plus aucune ecriture dans le repertoire courant : tempfile() +
  on.exit(unlink()) partout (politique CRAN, deja revendiquee par le
  NEWS 0.4.4)
- fixtures orphelines rules.yaml / test.yaml supprimees (referencees par
  aucun test)
- test-huge-parquet.R inexecutable partout (skip + chemins Windows en
  dur) deplace vers dev/manual-tests/
- test-extraction-params : les expect_ conditionnels silencieux
  assertent d'abord leur precondition (un check de cap ne peut plus
  passer a vide)
- tests unitaires directs des petits helpers (format_key_examples,
  get_col_names, is_non_local/is_arrow, branche file = NULL du rapport)

Non fait sciemment (documente NEWS) : la compression des redondances
(-30% vise par l'issue) - refactoring a fort churn de tests verts,
passe dediee.
Review interne : REQUEST CHANGES applique (ecriture cwd survivante). Copilot : premiere passe sans commentaire.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Cette MR “roll-up” pour la 0.4.10 regroupe une série de correctifs d’audit visant à aligner la sémantique (local vs lazy/SQL), durcir la robustesse, améliorer les performances sur gros volumes (lazy), et remettre la documentation/tests en phase avec le comportement réel du package.

Changes:

  • Alignement de sémantiques et durcissement d’API (NA unilatéral, clés absentes/invalides, precedence args vs YAML, equal_mode: normalized effectif).
  • Optimisations majeures du chemin lazy (agrégats SQL pour verdict/coverage, réduction des collect, hygiène des temp tables, SQL Parquet mieux échappé).
  • Nettoyage qualité (tests, doc/vignette/README, NEWS, packaging/imports, constantes centralisées).

Reviewed changes

Copilot reviewed 48 out of 56 changed files in this pull request and generated no comments.

Show a summary per file
File Description
vignettes/datadiff.Rmd Mise à jour de la vignette (lazy all-pass placeholder, diagnostics d’extraits, utilitaires exportés, langue par défaut, caveat NaN).
tests/testthat/test.yaml Suppression d’une fixture YAML orpheline.
tests/testthat/test-yaml-arg-precedence.R Nouveaux tests sur precedence args vs YAML (label/key) + validation d’inputs.
tests/testthat/test-utils.R Tests `%
tests/testthat/test-tolerance-ok.R Tests d’équivalence kernel vs fast-path intermédiaire (NA/NaN sans Inf).
tests/testthat/test-temp-tables.R Tests d’hygiène des temp tables lazy + unicité des noms.
tests/testthat/test-report-robustness.R Tests de robustesse report (memoization partagée, propagation eval_error).
tests/testthat/test-report-extracts-dir.R Tests extracts_dir (écriture CSV, pas de dir en all-pass, sanitization).
tests/testthat/test-pointblank-setup.R Ajustements tests suite dépréciation cols_reference + sémantique NA via __eq.
tests/testthat/test-nan-inf-sql-equivalence.R Tests d’équivalence SQL (DuckDB/SQLite) vs kernel R sur NaN/Inf/NA.
tests/testthat/test-na-equality-semantics.R Tests d’alignement sémantique NA des colonnes d’égalité (local vs lazy).
tests/testthat/test-main.R Tests read_rules() (versions, champs inconnus, sections non-mapping) + template version.
tests/testthat/test-lazy-aggregates.R Tests perf/contrat lazy (pas de collect en vert, collect des colonnes en échec seulement, SQLite).
tests/testthat/test-key-parameter.R Nettoyage tests key + erreurs franches colonnes de clé absentes + rejet clés vides/non-character.
tests/testthat/test-internal-helpers.R Tests unitaires directs helpers internes (format_key_examples, get_col_names, etc.).
tests/testthat/test-input-validation.R Tests write_rules_template/label avec tempfile + label “comparison”.
tests/testthat/test-factor-columns.R Tests de normalisation des facteurs (local + Arrow dictionary).
tests/testthat/test-extraction-params.R Durcissement tests extraction (plus d’assertions conditionnelles vacues).
tests/testthat/test-equivalence-guard.R Mise à jour garde d’équivalence (nouvelle sémantique NA unilatéral en égalité).
tests/testthat/test-edge-cases.R Durcissement positional path (erreur claire, no-collect avant stop, mix local/lazy).
tests/testthat/test-duplicate-keys.R Tests lazy sur colonne n + collision count + lazy_nrow + e2e.
tests/testthat/test-coverage.R Ajustement commentaire cohérent avec la nouvelle dérivation de verdict.
tests/testthat/test-boolean-column-guards.R Tests de garde (colonnes bool manquantes => erreur interne explicite).
tests/testthat/test-avoidable-scans.R Tests sur scans évitables (count_rows, colonnes ignorées hors join/extraits, agrégation doublons).
tests/testthat/test-arrow-dataset-to-duckdb.R Tests SQL Parquet (quote dans path, union_by_name, validation duckdb_memory_limit).
tests/testthat/rules.yaml Suppression d’une fixture YAML orpheline.
README.Rmd Quick Start zéro-config + Dev part non figée (commandes plutôt que sorties périssables).
README.md Idem README.Rmd + retouches tables/typos d’échappement Markdown.
R/validation.R lazy_nrow() robuste aux colonnes n + validate_row_counts(count_rows=).
R/utils.R `%
R/tolerance.R Kernel simplifié (NaN masks retirés), fast-path intermédiaire, SQL booleans NaN/Inf, renommages & helpers.
R/report.R Hardening report + defaults via constantes + memoization partagée + extracts_dir + écriture CSV d’extraits.
R/preprocessing.R normalize_text() simplifié + facteurs local -> character + equal_mode: normalized implicatif + lazy mutate composé.
R/pointblank_setup.R Dépréciation cols_reference + steps égalité via bool __eq + colonnes dummy via constantes.
R/globals.R Suppression globalVariables("n").
R/fast_path.R Gardes explicites colonnes bool manquantes + sémantique NA égalité alignée.
R/duplicate_keys.R Agrégation lazy en SQL + nom de count réservé pour éviter collisions avec colonne n.
R/data_types.R Doc: bucket “character” intentionnel (facteurs, types comparables).
R/coverage.R lazy_boolean_counts() (agrégats SQL) + build_coverage(counts=) + comptes en numeric (anti-overflow).
R/constants.R Nouvelles constantes centralisées (suffix/prefix colonnes, defaults warn/stop, helpers naming).
NEWS.md Changelog détaillé (features/perf/robustness/bugs/breaking changes).
NAMESPACE `%
man/write_rules_template.Rd Doc: character_equal_mode=normalized implique trim/case sauf override + inertie types non-character.
man/validate_row_counts.Rd Doc: nouveau paramètre count_rows.
man/setup_pointblank_agent.Rd Doc: cols_reference=NULL + dépréciation + contrat __eq/__ok.
man/preprocess_dataframe.Rd Doc: conversion facteurs local -> character + détail comportement.
man/or_operator.Rd Suppression doc de l’opérateur `%
man/detect_column_types.Rd Doc: règle bucket “character” et limites (list-cols non supportées).
man/datadiff_report_html.Rd Doc: extracts_dir + comportement.
man/compare_datasets_from_yaml.Rd Doc: validation clé absente, precedence args vs YAML, duckdb_memory_limit validé, @return corrigé.
inst/templates/rules_template.yaml Suppression template orphelin.
dev/RFC-api-0.5.md RFC design API 0.5 (doc uniquement, hors tarball).
dev/manual-tests/huge-parquet.R Déplacement test “huge parquet” vers dev/manual-tests.
DESCRIPTION Nettoyage Imports (retrait stats).
.Rbuildignore Exclusion de dev/ du tarball.
Files not reviewed (7)
  • man/compare_datasets_from_yaml.Rd: Generated file
  • man/datadiff_report_html.Rd: Generated file
  • man/detect_column_types.Rd: Generated file
  • man/preprocess_dataframe.Rd: Generated file
  • man/setup_pointblank_agent.Rd: Generated file
  • man/validate_row_counts.Rd: Generated file
  • man/write_rules_template.Rd: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…ecie

Le resultat de compare_datasets_from_yaml() porte la classe datadiff_result :
ses methodes $ et [[ redirigent l'ancien nom reponse vers response avec un
warning de depreciation (une fois par session), suppression prevue dans une
release future. datadiff_report_html() accepte encore un resultat sauvegarde
par une ancienne version. Le code qui inspecte names(result) doit migrer.

BREAKING CHANGE: le champ reponse devient response ; l'alias ne figure pas
dans names(result).
Vestige emis sur chaque comparaison positionnelle par defaut ; la sortie
utilisateur du cas nominal redevient silencieuse. Aucun test n'en dependait.
run_compare() rejoue chaque cas de la grille sur DuckDB lazy et exige
l'accord sur le verdict, les colonnes en echec et le nombre de lignes en
echec par colonne. Le pinning par cellule (tuples de cle) reste local-only :
les extraits lazy ne portent que les colonnes booleennes, sans cle ni
valeur (etat identique sur CRAN 0.5.0, documente dans la vignette).
Lookup des steps par id (vs$i) plutot que par position, et nettoyage du
YAML temporaire en sortie de run_compare().
Le CRAN a recu un 0.5.0 (consolidation, publie le 2026-06-18) sans bump
dans le depot. Le NEWS reinsere la section 0.5.0, cale la baseline des
breaking changes sur 0.5.0 et reclasse le retrait de l'export %||% en
breaking change. Version 0.6.0 : superieure au CRAN et bump mineur du fait
des retraits d'API (reponse, %||%).
@VincentGuyader VincentGuyader changed the title datadiff 0.4.10 : traitement des 24 issues ouvertes de l'audit datadiff 0.6.0 : traitement des 24 issues ouvertes de l'audit Jul 12, 2026
@VincentGuyader

Copy link
Copy Markdown
Member Author

Complement release 0.6.0 (5 commits, fe063f6..bb0fe6a) :

  • feat! : champ resultat reponse renomme response, avec classe datadiff_result dont les accesseurs $ et [[ gardent reponse lisible une release (warning de depreciation une fois par session). datadiff_report_html() accepte les resultats sauvegardes par les anciennes versions.
  • fix : retrait du message de debug "key is missing" sur le chemin positionnel.
  • test : la grille d'equivalence tourne desormais sur les deux backends (local + DuckDB lazy) : accord exige sur verdict, colonnes en echec et nombre de lignes en echec par colonne. Constat au passage : les extraits lazy ne portent ni cles ni valeurs (etat identique sur CRAN 0.5.0, verifie par reprex) ; le pinning par cellule reste local-only, documente dans la vignette. Candidat RFC 0.5 si besoin des cles en lazy.
  • chore(release) : version 0.6.0 (le CRAN est a 0.5.0), section NEWS 0.5.0 reinseree, baseline des breaking changes calee sur 0.5.0, retrait de l'export %||% reclasse en breaking.
  • chore(dev) : harnais de bench CRAN vs branche dans dev/bench-0.4.10/ (exclu du tarball).

Verifications : TDD red-first sur rename et message ; suite complete verte ; R CMD check --as-cran sur le tarball 0.6.0 : Status OK (0 erreur / 0 warning / 0 note) ; review interne pr-reviewer : APPROVE. Le tag/release GitHub v0.5.0 manquant a ete cree sur 9254abb (commit correspondant au tarball CRAN).

@VincentGuyader
VincentGuyader merged commit 1a6d32b into main Jul 12, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants