Remplace le stub 501 de src/mcp/oauth/authorize.py par le flux réel
authlib (GET consentement, POST émission de code, échange de token),
avec gate d'abonnement avant tout affichage du consentement.
Deux ajustements de compatibilité authlib 1.7.2 dans server.py,
découverts en exécutant le flux bout-en-bout pour la première fois :
AUTHLIB_INSECURE_TRANSPORT en mode DEVELOPMENT (le client de test
Flask ne sert pas en HTTPS) et OAUTH2_REFRESH_TOKEN_GENERATOR (off
par défaut côté Flask, requis pour émettre les refresh_token du scope
offline_access). Détails dans .superpowers/sdd/task-9-report.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- AuthorizationCodeGrant avec PKCE requis (CodeChallenge), RefreshTokenGrant
avec rotation + re-vérification d'abonnement, DCR (ClientRegistrationEndpoint)
et révocation (RevocationEndpoint) câblés sur le store sqlite (Tasks 1-3),
metadata (Task 5) et consent.subscription_ok (Task 6).
- _Token (dict + check_client/get_scope) fait le pont entre les rows sqlite
bruts et le protocole TokenMixin qu'authlib 1.7.2 attend pour le refresh
et la révocation.
test_002_filter_persistence et test_015_org_pages_filter_date ciblaient
encore le DataTable historique (.marches_table th[data-dash-column] input)
sur /acheteurs et /titulaires ; ces deux pages rendent désormais une
grille AG Grid (src/utils/entity_grid.py). Adapte les deux tests au
filtre flottant AG Grid (col-id + .ag-floating-filter-input), sur la
colonne texte "objet" (le filtre de date AG Grid utilise un <input
type="date"> natif peu fiable à piloter via Selenium). test_002 vérifie
en plus que la persistance du filtre est bien scopée par fiche
(entity_id/year) en rechargeant la même URL.
test_marches_table_hscroll_bar_present (tests/test_tableau_hscroll.py)
ciblait aussi /acheteurs/123 pour couvrir .marches_table + table_hscroll.js
: ce comportement n'est plus exercé par aucune page migrée. Le seul repli
restant (observatoire.py, hors périmètre #41/Lot 2b) a un bug préexistant
sans rapport qui empêche son rendu en Selenium sur ce jeu de données ;
le test est donc marqué skip avec la justification détaillée, en attendant
soit la correction de ce bug séparé, soit le retrait de cette couverture.
test_003_tableau_download n'a pas nécessité de changement (callbacks
"toutes les données" inchangés).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- ag_grid() à paramétrer (id dict + persisted_props), export_dataframe() à
étendre (base_where_sql/base_params) : extensions rétro-compatibles Lot 1
- reset efface filtres ET tris (approche #47-safe, préserve largeur/ordre)
- compteur "X marchés (Y lignes)" reproduit depuis /tableau (total_unique)
- columnState relu depuis le store partagé (pas le State grille), anti-boucle
alwaysShowHorizontalScroll force la piste de scroll à rester visible
en permanence (comportement déjà natif sous Chrome). Sans effet sur
Firefox, dont le curseur en overlay est géré par l'OS/le navigateur
et non contrôlable côté appli (#41).
Deux bugs découverts en usage réel sur /tableau :
1. filtermodel_to_ast lisait condition1/condition2 pour un filtre à
deux conditions (ET/OU) sur une colonne — mais AG Grid >=29.2 (la
version 35.2.0 utilisée ici) encode ça via une liste `conditions`,
confirmé par la doc Dash AG Grid "Filter Model & Dash Callbacks" >
"Filter Model Multiple Conditions". Le filtre était donc
silencieusement ignoré (colonne exclue du AST, comme si aucun
filtre n'était posé), d'où des résultats sans rapport avec les
valeurs saisies. Corrigé : filtermodel_to_ast lit désormais
`conditions` (N éléments) en priorité, avec repli sur
condition1/condition2 (forme dépréciée mais "still accepted" selon
AG Grid). L'inverse (ast_to_filtermodel, utilisé au rappel d'une
vue sauvegardée) produit aussi la forme `conditions`.
2. Le bouton "Réinitialiser" ("Supprime tous les filtres et les tris")
ne réinitialisait que filterModel, jamais le tri. Corrigé via
resetColumnState (remet les colonnes à l'état de columnDefs, qui
reflète déjà la visibilité choisie via le sélecteur de colonnes —
seul le tri est donc affecté).
Reproduit et corrigé en TDD (tests/test_query_ast.py,
tests/test_grid.py), vérifié manuellement en navigateur par l'auteur
du rapport de bug.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Menu de filtre de colonne (Contient, Égal à, Vide, ET/OU, etc.) traduit
via l'option native AG Grid localeText, exposée par dashGridOptions.
N'affecte pas l'apparence de base conservée au Lot 1 (aucun thème/CSS
custom) — uniquement le texte des libellés.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Les tests qui démarrent l'app complète (Selenium) écrivent dans la base
users pointée par USERS_DB_PATH (comptes créés, vues sauvegardées...).
Comme ce fichier était le fixture committé lui-même, chaque exécution de
la suite le laissait modifié dans l'arbre de travail — gênant pour les
commits et le nettoyage de worktree.
Même schéma déjà utilisé pour DUCKDB_PATH et DATA_SCHEMA_CACHE dans ce
fichier : le fixture committé est copié vers une base jetable gitignorée
(tests/users.runtime.sqlite) au chargement de conftest.py, et
USERS_DB_PATH pointe sur la copie. tests/users.test.sqlite reste
désormais toujours propre.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ajoute des tests de bout en bout pour les filtres montant/date/cpv, le
chemin titulaire de search_organisations, et la pagination (page>1,
clamping page=0). Normalise montant_total en float dans les deux
branches de compute_org_stats.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>